Search code examples
iosobjective-cuitextfielduipickerviewuipickerviewdelegate

UIPickerView selectedRowInComponent:0 always return zero first when I try to set data in second component


I'm new to iOS programming.

Recently, I'm trying to make a UIPickerView as an inputView in UITextfield.

The data in UIPickerView is about all the iOS built-in fonts. So I want to make two components in UIPickerView: the first is familyType, and the second is all the fonts in that familyType.

I simulate the code from this answer, but I meet some problem I can't solve. Any help is welcome!

My question is here:
Why rowOneSelected in this function always get 0 first, even I use selectedRowInComponent in advance?

// The number of rows of data
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    if(component == 0)
    {
        return _fontTypeArray.count;
    }
    else
    {
        NSInteger rowOneSelected = [_pickerFont selectedRowInComponent:0];
        FontType *temp = _fontTypeArray[rowOneSelected];
        NSLog(@"%ld", (long)rowOneSelected);    // I use this to debug, and there is a main question: why every time it logs 0 first?
        return temp.font.count;
    }
}





All my relative code is here:

In ViewController.h:

#import <UIKit/UIKit.h>
#import "MenuLayerTwoPlusThree.h"

@interface ViewController : UIViewController

@property MenuLayerTwoPlusThree *layerTwoPlusThree;

- (void)createLayerTwoPlusThree;

@end

In ViewController.m:

- (void)viewDidLoad {
    [super viewDidLoad];

    [self createLayerTwoPlusThree];
}

- (void)createLayerTwoPlusThree
{
    _layerTwoPlusThree = [MenuLayerTwoPlusThree alloc];

    [_layerTwoPlusThree createFontArray];
    [_layerTwoPlusThree createSelectPanel];
}

In FontType.h:

#ifndef FontType_h
#define FontType_h

#import <Foundation/Foundation.h>

@interface FontType : NSObject

@property NSString *familyName;
@property NSMutableArray *font;

@end

#endif /* FontType_h */

In FontType.m:

#import <Foundation/Foundation.h>
#import "FontType.h"

@implementation FontType

@synthesize familyName;
@synthesize font;

@end

In MenuLayerTwoPlusThree.h:

#ifndef MenuLayerTwoPlusThree_h
#define MenuLayerTwoPlusThree_h

#import <UIKit/UIKit.h>
#import "FontType.h"

@interface MenuLayerTwoPlusThree : NSObject<UITextFieldDelegate, UIPickerViewDataSource, UIPickerViewDelegate>

@property UITextField *textFieldFont;    
@property NSMutableArray *fontTypeArray;
@property UIPickerView *pickerFont;
@property UIBarButtonItem *doneButton;
@property UIBarButtonItem *spaceButton;
@property UIBarButtonItem *cancelButton;
@property UIToolbar *toolBar;
@property NSArray *toolBarItems;
@property NSInteger familyType;
@property NSInteger fontType;
@property NSString *fontName;
- (void)createFontArray;
- (IBAction)pickerViewButtonClicked:(id)sender;

@end

In MenuLayerTwoPlusThree.m

- (void)createFontArray
{
    _fontTypeArray = [[NSMutableArray alloc]initWithCapacity:80];

    int number = 0;

    for(NSString* family in [UIFont familyNames])
    {
        //NSLog(@"%@", family);
        //number++;
        FontType *temp = [[FontType alloc]init];
        temp.familyName = family;
        temp.font = [[NSMutableArray alloc]init];
        int flag = 0;

        for(NSString* name in [UIFont fontNamesForFamilyName:family])
        {
            //NSLog(@" %@", name);
            //number++;
            flag++;
            [temp.font addObject:name];
        }

        // add Heiti SC, Heiti TC, Telugu Sangam MN, and Bangla Sangam MN to font array
        if(flag == 0)
        {
            [temp.font addObject:family];
        }

        [_fontTypeArray addObject:temp];
    }


    // print all fonts test
    for(FontType *x in _fontTypeArray)
    {
        number++;
        NSLog(@"%@", x.familyName);
        for(NSString *y in x.font)
        {
            //number++;
            NSLog(@"\t%@", y);
        }
    }      

    NSLog(@"//////////////////////////////");
    NSLog(@"%d", number);
}

- (void)createSelectPanel
{
    [self createSelectPanelForPancel1];
    [self createSelectPanelForPancel2];
    [self createSelectPanelForFont];
    [self createSelectPanelForShape];
    [self createSelectPanelForEraser];
}

- (void)createSelectPanelForFont
{
    _textFieldFont = [[UITextField alloc]initWithFrame:CGRectMake(19, 148, 150, 12)];
    [_textFieldFont setBackground:[UIImage imageNamed:@"font-type-bar.png"]];
    _textFieldFont.rightViewMode = UITextFieldViewModeAlways;
    _textFieldFont.delegate = self;
    [_textFieldFont setPlaceholder:@"Heiti TC"];
    _textFieldFont.font = [_textFieldFont.font fontWithSize:10 * _aspectRatio];


    // resize right view image
    UIImageView *rightViewImage = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 12 * _aspectRatio, 12 * _aspectRatio)];
    [rightViewImage setImage:[UIImage imageNamed:@"font-type-bar-roll.png"]];
    _textFieldFont.rightView = rightViewImage;


    _pickerFont = [[UIPickerView alloc]init];
    _pickerFont.dataSource = self;
    _pickerFont.delegate = self;
    _pickerFont.showsSelectionIndicator = YES;


    _doneButton = [[UIBarButtonItem alloc]initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(pickerViewButtonClicked:)];
    _spaceButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
    _cancelButton = [[UIBarButtonItem alloc]initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(pickerViewButtonClicked:)];
    _toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 45)];
    [_toolBar setBarStyle:UIBarStyleDefault];
    _toolBarItems = [NSArray arrayWithObjects:_cancelButton, _spaceButton, _doneButton, nil];
    [_toolBar setItems:_toolBarItems];

    _textFieldFont.inputView = _pickerFont;
    _textFieldFont.inputAccessoryView = _toolBar;
    if (@available(iOS 9.0, *)) {
        _textFieldFont.inputAssistantItem.leadingBarButtonGroups = @[];
    } else {
        // Fallback on earlier versions
    }
    if (@available(iOS 9.0, *)) {
        _textFieldFont.inputAssistantItem.trailingBarButtonGroups = @[];
    } else {
        // Fallback on earlier versions
    }

    // I want to add these codes to select row in advanced to make sure first time NSLog will print 9, but it doesn't work.
//    [_pickerFont reloadAllComponents];
//    _familyType = 9;
//    _fontType = 0;
//    _fontName = @"Heiti TC";
//    [_pickerFont selectRow:_familyType inComponent:0 animated:YES];

    [_selectPanelFontView addSubview:_textFieldFont];
}

And this is the delegate, which I am written in the MenuLayerTwoPlusThree.m:

// The number of columns of data
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 2;
}

// The number of rows of data
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    if(component == 0)
    {
        return _fontTypeArray.count;
    }
    else
    {
        NSInteger rowOneSelected = [_pickerFont selectedRowInComponent:0];
        FontType *temp = _fontTypeArray[rowOneSelected];
        NSLog(@"%ld", (long)rowOneSelected);    // I use this to debug, and there is a main question: why every time it logs 0 first?
        return temp.font.count;
    }
}

// The data to return for the row and component (column) that's being passed in
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    if(component == 0)
    {
        FontType *temp = _fontTypeArray[row];
        return temp.familyName;
    }
    else
    {
        NSInteger rowOneSelected = [_pickerFont selectedRowInComponent:0];
        FontType *temp = _fontTypeArray[rowOneSelected];
        return [temp.font objectAtIndex:row];
    }
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    if(component == 0)
    {
        [_pickerFont reloadComponent:1];
    }

    // This block is moved to pickerViewButtonClicked (sender == _doneButton)
//    else
//    {
//        NSInteger rowOneSelected = [_pickerFont selectedRowInComponent:0];
//        FontType *temp = _fontTypeArray[rowOneSelected];
//        [_textFieldFont setText:temp.font[row]];
//    }
}

- (IBAction)pickerViewButtonClicked:(id)sender
{
    if(sender == _doneButton)
    {
        // Save value when I clicked the done button.
        _familyType = [_pickerFont selectedRowInComponent:0];
        _fontType = [_pickerFont selectedRowInComponent:1];

//        NSLog(@"family: %ld", _familyType);
//        NSLog(@"font: %ld", _fontType);

        FontType *temp = _fontTypeArray[_familyType];
        _fontName = temp.font[_fontType];
        [_textFieldFont setText:_fontName];

//        NSLog(@"font name: %@", _fontName);


        [_textFieldFont endEditing:YES];
    }
    else if(sender == _cancelButton)
    {
        [_textFieldFont endEditing:YES];

        // I want to turn back to the last selected value when I clicked the cancel button.
        [_pickerFont reloadAllComponents];
        [_pickerFont selectRow:_familyType inComponent:0 animated:NO];
        [_pickerFont selectRow:_fontType inComponent:1 animated:NO];
    }
}

Solution

  • It really seems that when the UIPickerView appears as an inputView after you have selected row 9 in component 0, the selectedRowInComponent: 0 returns 0 in numberOfRowsInComponent: 1, but then returns 9 in titleForRow: row forComponent: 1.

    I don't think you are doing anything wrong, so it looks like a bug in UIKit.

    As a workaround, I suggest you don't ask the picker view for the selected row, but track the selected row yourself (initialize your own variable in your view controller and update it in didSelectRow: row inComponent: 0)