当数据源没有数据时,自定义UIPickerView崩溃

时间:2013-10-14 21:34:58

标签: ios objective-c uitextfield uipickerview

我有一个带有两个文本字段(类别,子类别)的屏幕,每个字段都连接到自定义UIPickerView。子类别视图中显示的选项取决于选择的类别作为第一个字段的值。

如果用户未选择类别,则选择子类别字段会显示标准键盘(此行为很好)。

如果用户选择一个类别,然后与子类别字段进行交互,一切正常。

当用户放入一个类别,调出子类别选择器,然后返回并清除类别字段时,就会出现问题。此时,如果用户选择子类别字段,则选择器将显示为没有任何数据,并且与其进行交互将导致应用程序崩溃。

错误文字:

*** Assertion failure in -[UITableViewRowData rectForRow:inSection:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableViewRowData.m:1630
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for rect at invalid index path (<NSIndexPath 0x719eb60> 2 indexes [0, 0])'
*** First throw call stack:
(0x1cc3012 0x1100e7e 0x1cc2e78 0xb96665 0x22df20 0xf12de 0x481086 0x480f7a 0xa440d 0xa69eb 0x30f85a 0x30e99b 0x3100df 0x312d2d 0x312cac 0x30aa28 0x77972 0x77e53 0x55d4a 0x47698 0x1c1edf9 0x1c1ead0 0x1c38bf5 0x1c38962 0x1c69bb6 0x1c68f44 0x1c68e1b 0x1c1d7e3 0x1c1d668 0x44ffc 0x2acd 0x29f5)
libc++abi.dylib: terminate called throwing an exception

这是我的代码:

- (IBAction)showYourPicker:(id)sender {
        isCategoryPicker = true;
        // create a UIPicker view as a custom keyboard view
        UIPickerView* pickerView = [[UIPickerView alloc] init];
        [pickerView sizeToFit];
        pickerView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
        pickerView.delegate = self;
        pickerView.dataSource = self;
        pickerView.showsSelectionIndicator = YES;
        self.catPickView = pickerView;  //UIPickerView

        categoryField.inputView = pickerView;

        // create a done view + done button, attach to it a doneClicked action, and place it in a toolbar as an accessory input view...
        // Prepare done button
        UIToolbar* keyboardDoneButtonView = [[UIToolbar alloc] init];
        keyboardDoneButtonView.barStyle = UIBarStyleBlack;
        keyboardDoneButtonView.translucent = YES;
        keyboardDoneButtonView.tintColor = nil;
        [keyboardDoneButtonView sizeToFit];

        UIBarButtonItem* doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done"
                                                                        style:UIBarButtonItemStyleBordered target:self
                                                                       action:@selector(pickerDoneClicked:)];

        [keyboardDoneButtonView setItems:[NSArray arrayWithObjects:doneButton, nil]];

        // Plug the keyboardDoneButtonView into the text field...
        categoryField.inputAccessoryView = keyboardDoneButtonView;
    }

    - (IBAction)showYourSubPicker:(id)sender {
        isCategoryPicker = false;
        WCSharedCache *sharedManager = [WCSharedCache sharedManager];
        BOOL iLLAllowIt = false;
        for(int i = 0; i < [sharedManager.categories count]; i++) {
            if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
                iLLAllowIt = true;
            }
        }
        if(!iLLAllowIt) {
            return;
        }
        // create a UIPicker view as a custom keyboard view
        UIPickerView* pickerView = [[UIPickerView alloc] init];
        [pickerView sizeToFit];
        pickerView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
        pickerView.delegate = self;
        pickerView.dataSource = self;
        pickerView.showsSelectionIndicator = YES;
        self.subCatPickView = pickerView;  //UIPickerView

        subcategoryField.inputView = pickerView;

        // create a done view + done button, attach to it a doneClicked action, and place it in a toolbar as an accessory input view...
        // Prepare done button
        UIToolbar* keyboardDoneButtonView = [[UIToolbar alloc] init];
        keyboardDoneButtonView.barStyle = UIBarStyleBlack;
        keyboardDoneButtonView.translucent = YES;
        keyboardDoneButtonView.tintColor = nil;
        [keyboardDoneButtonView sizeToFit];

        UIBarButtonItem* doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done"
                                                                       style:UIBarButtonItemStyleBordered target:self
                                                                      action:@selector(pickerDoneClicked:)];

        [keyboardDoneButtonView setItems:[NSArray arrayWithObjects:doneButton, nil]];

        // Plug the keyboardDoneButtonView into the text field...
        subcategoryField.inputAccessoryView = keyboardDoneButtonView;
    }

    - (void) pickerDoneClicked: (id) picker {
        if(isCategoryPicker) {
            [categoryField resignFirstResponder];
        } else {
            [subcategoryField resignFirstResponder];
        }
    }

    - (void) pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
        WCSharedCache *sharedManager = [WCSharedCache sharedManager];
        if(isCategoryPicker) {
            [categoryField setText:[[sharedManager categories] objectAtIndex:row]];
        } else {
            @try {
                int idx = 0;
                for(int i = 0; i < [sharedManager.categories count]; i++) {
                    if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
                        idx = i;
                        break;
                    }
                }
                [subcategoryField setText:[[[sharedManager subcategories]objectAtIndex:idx] objectAtIndex:row]];
            } @catch (NSException *e) {
                NSLog(@"Exception: %@",e);
               [subcategoryField setText:@"" ];
            }
        }
    }

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

    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
    {
        WCSharedCache *sharedManager = [WCSharedCache sharedManager];
        if(isCategoryPicker) {
            return [[sharedManager categories]count];
        } else {
            for(int i = 0; i < [sharedManager.categories count]; i++) {
                if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
                    return [[[sharedManager subcategories] objectAtIndex:i] count];
                }
            }
        }
        return 0;
    }

    - (NSString *)pickerView: (UIPickerView *)pickerView titleForRow: (NSInteger)row forComponent:(NSInteger)component
    {
        WCSharedCache *sharedManager = [WCSharedCache sharedManager];
        if(isCategoryPicker) {
            return [[sharedManager categories] objectAtIndex:row];
        } else {
            for(int i = 0; i < [sharedManager.categories count]; i++) {
                if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
                    return [[[sharedManager subcategories] objectAtIndex:i] objectAtIndex:row];
                }
            }
        }
        return @"";
    }

是否有我可以实现的方法,try-catch,或原始键盘的复兴,或者我可以阻止用户在没有任何数据的情况下使用该字段。类别的数据在NSArray中。子类别的数据位于二维NSArray中,该索引是从相关类别的索引开始的。

1 个答案:

答案 0 :(得分:0)

如果通过更改以下内容中的默认值来解决此问题:

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
    {
        WCSharedCache *sharedManager = [WCSharedCache sharedManager];
        if(isCategoryPicker) {
            return [[sharedManager categories]count];
        } else {
            for(int i = 0; i < [sharedManager.categories count]; i++) {
                if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
                    return [[[sharedManager subcategories] objectAtIndex:i] count];
                }
            }
        }
        return 1;
    }

从零到一。显然,如果组件中的行数为0,UIPickerView(至少在上面的实现中)无法处理滚动而不抛出异常。这很奇怪,因为根据Apple's documentation of the UIPickerView class默认此方法的值为0。