it-swarm.asia

UIPickerView'ın seçim çubuğundaki sabit etiketler

Saatler uygulamasında, zamanlayıcı ekranı seçim çubuğunda bir metin (bu durumda "saat" ve "dakika") bulunan bir metin seçiciyi (muhtemelen UIPicker modunda bir UIDatePickerModeCountDownTimer) gösterir.

(düzenleme) Bu etiketlerin sabit olduğuna dikkat edin: Seçici tekerlek dönerken hareket etmiyorlar.

Bu tür sabit etiketleri standart bir UIPickerView bileşeninin seçim çubuğunda göstermenin bir yolu var mı?

Bu konuda yardımcı olacak herhangi bir API bulamadım. Bir öneri, seçicinin alt görünümü olarak UILabel eklemekti, ancak bu işe yaramadı.


Cevap

Ed Marty'nin tavsiyesine uydum (cevap aşağıda) ve işe yarıyor! Mükemmel değil ama insanları kandırmalı. Başvuru için, işte benim uygulama, daha iyi yapmak için çekinmeyin ...

- (void)viewDidLoad {
    // Add pickerView
    self.pickerView = [[UIPickerView alloc] initWithFrame:CGRectZero];
    [pickerView release];
    CGSize pickerSize = [pickerView sizeThatFits:CGSizeZero];
    CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
    #define toolbarHeight           40.0
    CGFloat pickerTop = screenRect.size.height - toolbarHeight - pickerSize.height;
    CGRect pickerRect = CGRectMake(0.0, pickerTop, pickerSize.width, pickerSize.height);
    pickerView.frame = pickerRect;

    // Add label on top of pickerView
    CGFloat top = pickerTop + 2;
    CGFloat height = pickerSize.height - 2;
    [self addPickerLabel:@"x" rightX:123.0 top:top height:height];
    [self addPickerLabel:@"y" rightX:183.0 top:top height:height];
    //...
}

- (void)addPickerLabel:(NSString *)labelString rightX:(CGFloat)rightX top:(CGFloat)top height:(CGFloat)height {
#define PICKER_LABEL_FONT_SIZE 18
#define PICKER_LABEL_ALPHA 0.7
    UIFont *font = [UIFont boldSystemFontOfSize:PICKER_LABEL_FONT_SIZE];
    CGFloat x = rightX - [labelString sizeWithFont:font].width;

    // White label 1 pixel below, to simulate embossing.
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x, top + 1, rightX, height)];
    label.text = labelString;
    label.font = font;
    label.textColor = [UIColor whiteColor];
    label.backgroundColor = [UIColor clearColor];
    label.opaque = NO;
    label.alpha = PICKER_LABEL_ALPHA;
    [self.view addSubview:label];
    [label release];

    // Actual label.
    label = [[UILabel alloc] initWithFrame:CGRectMake(x, top, rightX, height)];
    label.text = labelString;
    label.font = font;
    label.backgroundColor = [UIColor clearColor];
    label.opaque = NO;
    label.alpha = PICKER_LABEL_ALPHA;
    [self.view addSubview:label];
    [label release];
}
31
squelart

Seçicinizi oluşturun, gölgeli bir etiket oluşturun ve onu selectionIndicator görünümünün altındaki bir seçicinin alt görünümüne itin. 

Böyle bir şey olurdu


UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(135, 93, 80, 30)] autorelease];
label.text = @"Label";
label.font = [UIFont boldSystemFontOfSize:20];
label.backgroundColor = [UIColor clearColor];
label.shadowColor = [UIColor whiteColor];
label.shadowOffset = CGSizeMake (0,1);
[picker insertSubview:label aboveSubview:[picker.subviews objectAtIndex:5]]; 
//When you have multiple components (sections)...
//you will need to find which subview you need to actually get under
//so experiment with that 'objectAtIndex:5'
//
//you can do something like the following to find the view to get on top of
// define @class UIPickerTable;
// NSMutableArray *tables = [[NSMutableArray alloc] init];
// for (id i in picker.subviews) if([i isKindOfClass:[UIPickerTable class]]) [tables addObject:i];
// etc...

- İleri öde

12
dizy

Dizinin cevabını birkaç yıl önce UIPickerViewkategorisine çevirdim. Sadece hala iOS SDK 4.3 ile çalıştığını ve buraya gönderdiğini doğruladı. Bir etiket eklemenize izin verir (XX saat) ve bu etiketteki değişiklikleri canlandırın (örneğin 1 saat -> 3 saat) tıpkı UIDatePickername__'de olduğu gibi.

// UIPickerView_SelectionBarLabelSupport.h
//
// This file adds a new API to UIPickerView that allows to easily recreate
// the look and feel of UIDatePicker labeled components.
//
// Copyright (c) 2009, Andrey Tarantsov <[email protected]>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


#import <Foundation/Foundation.h>


// useful constants for your font size-related code
#define kPickerViewDefaultTitleFontSize 20.0f
#define kDatePickerTitleFontSize 25.0f
#define kDatePickerLabelFontSize 21.0f


@interface UIPickerView (SelectionBarLabelSupport)

// The primary API to add a label to the given component.
// If you want to match the look of UIDatePicker, use 21pt as pointSize and 25pt as the font size of your content views (titlePointSize).
// (Note that UIPickerView defaults to 20pt items, so you need to use custom views. See a helper method below.)
// Repeated calls will change the label with an animation effect similar to UIDatePicker's one.
//
// To call this method on viewDidLoad, please call [pickerView layoutSubviews] first so that all subviews
// get created.
- (void)addLabel:(NSString *)label ofSize:(CGFloat)pointSize toComponent:(NSInteger)component leftAlignedAt:(CGFloat)offset baselineAlignedWithFontOfSize:(CGFloat)titlePointSize;

// A helper method for your delegate's "pickerView:viewForRow:forComponent:reusingView:".
// Creates a propertly positioned right-aligned label of the given size, and also handles reuse.
// The actual UILabel is a child of the returned view, use [returnedView viewWithTag:1] to retrieve the label.
- (UIView *)viewForShadedLabelWithText:(NSString *)label ofSize:(CGFloat)pointSize forComponent:(NSInteger)component rightAlignedAt:(CGFloat)offset reusingView:(UIView *)view;

// Creates a shaded label of the given size, looking similar to the labels used by UIPickerView/UIDatePicker.
- (UILabel *)shadedLabelWithText:(NSString *)label ofSize:(CGFloat)pointSize;

@end

Ve uygulama:

// UIPickerView_SelectionBarLabelSupport.m
//
// This file adds a new API to UIPickerView that allows to easily recreate
// the look and feel of UIDatePicker labeled components.
//
// Copyright (c) 2009, Andrey Tarantsov <[email protected]>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#import "UIPickerView_SelectionBarLabelSupport.h"


// used to find existing component labels among UIPicker's children
#define kMagicTag 89464534
// a private UIKit implementation detail, but we do degrade gracefully in case it stops working
#define kSelectionBarClassName @"_UIPickerViewSelectionBar"

// used to sort per-component selection bars in a left-to-right order
static NSInteger compareViews(UIView *a, UIView *b, void *context) {
    CGFloat ax = a.frame.Origin.x, bx = b.frame.Origin.x;
    if (ax < bx)
        return -1;
    else if (ax > bx)
        return 1;
    else
        return 0;
}


@implementation UIPickerView (SelectionBarLabelSupport)

- (UILabel *)shadedLabelWithText:(NSString *)label ofSize:(CGFloat)pointSize {
    UIFont *font = [UIFont boldSystemFontOfSize:pointSize];
    CGSize size = [label sizeWithFont:font];
    UILabel *labelView = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)] autorelease];
    labelView.font = font;
    labelView.adjustsFontSizeToFitWidth = NO;
    labelView.shadowOffset = CGSizeMake(1, 1);
    labelView.textColor = [UIColor blackColor];
    labelView.shadowColor = [UIColor whiteColor];
    labelView.opaque = NO;
    labelView.backgroundColor = [UIColor clearColor];
    labelView.text = label;
    labelView.userInteractionEnabled = NO;
    return labelView;
}

- (UIView *)viewForShadedLabelWithText:(NSString *)title ofSize:(CGFloat)pointSize forComponent:(NSInteger)component rightAlignedAt:(CGFloat)offset reusingView:(UIView *)view {
    UILabel *label;
    UIView *wrapper;
    if (view != nil) {
        wrapper = view;
        label = (UILabel *)[wrapper viewWithTag:1];
    } else {
        CGFloat width = [self.delegate pickerView:self widthForComponent:component];

        label = [self shadedLabelWithText:title ofSize:pointSize];
        CGSize size = label.frame.size;
        label.frame = CGRectMake(0, 0, offset, size.height);
        label.tag = 1;
        label.textAlignment = UITextAlignmentRight;
        label.autoresizingMask = UIViewAutoresizingFlexibleHeight;

        wrapper = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, width, size.height)] autorelease];
        wrapper.autoresizesSubviews = NO;
        wrapper.userInteractionEnabled = NO;
        [wrapper addSubview:label];
    }
    label.text = title;
    return wrapper;
}

- (void)addLabel:(NSString *)label ofSize:(CGFloat)pointSize toComponent:(NSInteger)component leftAlignedAt:(CGFloat)offset baselineAlignedWithFontOfSize:(CGFloat)titlePointSize {
    NSParameterAssert(component < [self numberOfComponents]);

    NSInteger tag = kMagicTag + component;
    UILabel *oldLabel = (UILabel *) [self viewWithTag:tag];
    if (oldLabel != nil && [oldLabel.text isEqualToString:label])
        return;

    NSInteger n = [self numberOfComponents];
    CGFloat total = 0.0;
    for (int c = 0; c < component; c++)
        offset += [self.delegate pickerView:self widthForComponent:c];
    for (int c = 0; c < n; c++)
        total += [self.delegate pickerView:self widthForComponent:c];
    offset += (self.bounds.size.width - total) / 2;

    offset += 2 * component; // internal UIPicker metrics, measured on a screenshot
    offset += 4; // add a gap

    CGFloat baselineHeight = [@"X" sizeWithFont:[UIFont boldSystemFontOfSize:titlePointSize]].height;
    CGFloat labelHeight = [@"X" sizeWithFont:[UIFont boldSystemFontOfSize:pointSize]].height;

    UILabel *labelView = [self shadedLabelWithText:label ofSize:pointSize];
    labelView.frame = CGRectMake(offset,
                                 (self.bounds.size.height - baselineHeight) / 2 + (baselineHeight - labelHeight) - 1,
                                 labelView.frame.size.width,
                                 labelView.frame.size.height);
    labelView.tag = tag;

    UIView *selectionBarView = nil;
    NSMutableArray *selectionBars = [NSMutableArray array];
    for (UIView *subview in self.subviews) {
        if ([[[subview class] description] isEqualToString:kSelectionBarClassName])
            [selectionBars addObject:subview];
    }
    if ([selectionBars count] == n) {
        [selectionBars sortUsingFunction:compareViews context:NULL];
        selectionBarView = [selectionBars objectAtIndex:component];
    }
    if (oldLabel != nil) {
        [UIView beginAnimations:nil context:oldLabel];
        [UIView setAnimationDuration:0.25];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationDidStopSelector:@selector(YS_barLabelHideAnimationDidStop:finished:context:)];
        oldLabel.alpha = 0.0f;
        [UIView commitAnimations];
    }
    // if the selection bar hack stops working, degrade to using 60% alpha
    CGFloat normalAlpha = (selectionBarView == nil ? 0.6f : 1.0f);
    if (selectionBarView != nil)
        [self insertSubview:labelView aboveSubview:selectionBarView];
    else
        [self addSubview:labelView];
    if (oldLabel != nil) {
        labelView.alpha = 0.0f;
        [UIView beginAnimations:nil context:oldLabel];
        [UIView setAnimationDuration:0.25];
        [UIView setAnimationDelay:0.25];
        labelView.alpha = normalAlpha;
        [UIView commitAnimations];
    } else {
        labelView.alpha = normalAlpha;
    }
}

- (void)YS_barLabelHideAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(UIView *)oldLabel {
    [oldLabel removeFromSuperview];
}

@end

Kullanım örneği (görünüm denetleyicisinde):

- (void)updateFloorLabel {
    NSInteger floor = [self.pickerView numberOfRowsInComponent:0] - [self.pickerView selectedRowInComponent:0];
    NSString *suffix = @"th";
    if (((floor % 100) / 10) != 1) {
        switch (floor % 10) {
            case 1:  suffix = @"st"; break;
            case 2:  suffix = @"nd"; break;
            case 3:  suffix = @"rd"; break;
        }
    }
    [self.pickerView addLabel:[NSString stringWithFormat:@"%@ Floor", suffix]
                       ofSize:21
                  toComponent:0
                leftAlignedAt:50
baselineAlignedWithFontOfSize:25];    
}

- (void)viewDidLoad {
  ...
  [self.pickerView layoutSubviews];
  [self updateFloorLabel];
  ...
}

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
    NSString *s = [NSString stringWithFormat:@"%d", [pickerView numberOfRowsInComponent:0] - row];
    return [pickerView viewForShadedLabelWithText:s ofSize:25 forComponent:0 rightAlignedAt:46 reusingView:view];
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
    [self updateFloorLabel];
}

Keyfini çıkarın!

11
Andrey Tarantsov

Diyelim ki mesafeyi seçmek için seçici bir görünüm uygulamak istediğimizi varsayalım, biri mesafe için biri, biri km olan 2 sütun vardır. Sonra ikinci sütunun düzeltilmesini istiyoruz. Bazı delege yöntemleri ile yapabiliriz.

- (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    if (component == 0) {
        return self.distanceItems[row];
    }
    else {
        return @"km";
    }
}

-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 2;
}

-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    if (component == 0) {
        return [self.distanceItems count];
    }
    else {
    // when it comes to the second column, only one row.
        return 1;
    }
}

Şimdi bizde bu var: enter image description here

Sanırım bu en basit yol.

7
Kun Hu

Yapabileceğin 2 şey var:

Satırdaki her satır ve bileşen basit bir metinse, varsayılan UIPickerView uygulamasını olduğu gibi kullanabilirsiniz; denetleyicinizde aşağıdaki UIPickerViewDelegateyöntemlerini kullanabilirsiniz:

  • Hangi satırın seçildiğini takip etmek için - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component 

  • seçili satır için - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component uygulamasındaki farklı bir metni döndür

Seçilen satır için farklılaştırıcı olarak metinden başka bir şeye sahip olmanız gerekiyorsa, temelde CustomPickerViewve sonrasında türetilen kendi UIPickerViewname__'nizi oluşturmanız gerekir. 

  • Önce - (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated işlevini uygulayın ve hangi satırın seçildiğini takip edin.

  • Ardından, seçilen satır için farklı bir görünüm oluşturmak üzere - (UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component öğesini uygulayın.

UIPickerView'ü kullanmak veya özel UIPickerView uygulamak için bir örnek, UICatalog adı verilen SDK'da mevcuttur.

4
keremk

İOS 7'de iyi çalışan bir yanıt aldım question , ki oldukça havalı trick .

Fikir, birden fazla bileşen oluşturmak ve bu etiket bileşenleri için bunun tek bir satır olduğunu belirtmektir. Bazı insanların sahip olduğu kabartmalı görünüm için NSAttributedStrings'i delege yöntemiyle geri verebilirsiniz: 

- (NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component 

3
Jon

UIPickerView içine bir etiket eklemek yerine, üst üste binen bir kardeş olarak etiketin üzerine yapıştırın. Sorunlu olan tek şey nasıl aynı fontu alacağı. Bu kabarık görünümü nasıl elde edeceğimi bilmiyorum, ama belki de başkası yapar, bu durumda, bu gerçekten bir sorun değildir.

1
Ed Marty

Ben de aynı problemle karşılaştım. Çalışma örneğini GitHub'da yayınlanan özel yapım zaman seçicimde görebilirsiniz:
https://github.com/kgadzinowski/iOSSecondsTimerPicker
Tam olarak istediğini yapar.

1
Konrad G

Etiketlerin kabartmalı görünümünü yeniden oluşturmak için ... Metin ile kolayca bir görüntü oluşturun, böylece metne kolayca benzer bir efekt uygulayabilirsiniz ... ve sonra Etiketler yerine UIImageView'leri kullanın

1
devguy

PickerTop ve pickerSize'ı nerede tanımladığınızı gösterebilir misiniz?

    CGFloat pickerTop = timePicker.bounds.Origin.y;
CGSize pickerSize = timePicker.bounds.size;

Sahip olduğum şey bu, ama toplayıcı yanlış görünüyor.

mikrofon

0
mikechambers

Xib editörünün de alt görünümler eklemesine izin verdiğine dikkat edin, böylece boyutlar üzerinde çok fazla kodlama ve tahmin kullanmaktan kaçınabilirsiniz.

0
Dmitriy R
0
boxed