4

其实,我之前也问过同样的问题,但我还没有找到解决方法,所以我又来了。

我的情况:

我有一个UIPickerView带有2个相关组件的,第二个或右侧组件中的行数和内容随着当前选择的第一或左侧组件中的行而变化,这是一个非常常见且有用的功能UIPickerView

现在,如果我使用 2 个拇指同时滚动两个组件,我的应用程序很快就会很容易崩溃,并显示以下信息:

*** Terminating app due to uncaught exception of class '_NSZombie_NSException'
libc++abi.dylib: terminate called throwing an exception

具有讽刺意味的是,我无法调试它。

实际上,如果我在方法中添加断点pickerView:didSelectRow:inComponent,我什至不能同时快速滚动两个组件,因为每次我将手指放在屏幕上时它都会在断点处停止。

我只能猜测原因是当第一个组件被滚动时,第二个组件中的假定行数不断变化,但与此同时,它UIPickerView正在询问第二个组件的标题和编号,然后它崩溃了。

但是我还没有找到任何可以用来判断一个组件是否被滚动的方法。所以我找不到正确的时间来拒绝pickerView'sdelegatedataSource第二个组件的请求。

有没有人遇到过类似的情况?

谢谢你的帮助!我会很感激的!


在我的代码中,这个错误是关于 的typePicker,所以我删除了所有其他与 . 无关的代码typePicker

在这里,typeand是 Core Data 中的两个实体,以及一个名为reach from todetailTypes的一对多关系。detailstypedetailsTypes

例如,如果类型是“国家”,detailTypes则将是“美国”、“法国”、“中国”等。

所以在 中typePicker,第 1 个或左边的组件显示所有类型实体,第 2 个或右边的组件detailTypes根据第 1 个组件中当前选择的类型显示相应的实体。

并且两个组件的第一行typePicker总是“None”,让用户不选择特定的类型,这就是代码中有很多“+1”的原因。

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
if (pickerView == self.typePicker)
        return 2;
    return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    NSUInteger majorTypeRow = [self.typePicker selectedRowInComponent:0] - 1;
    if (pickerView == self.typePicker) {
        if (component == 0)
            return [self.types count] + 1;
        else {
            if (majorTypeRow == -1) // no major type entities
                return 1;
            NSString *majorTypeName = [[self.types objectAtIndex:majorTypeRow] name];
            NSArray *detailType = [self.detailTypes objectForKey:majorTypeName];
            if (detailType) 
                return [detailType count] + 1;
            else {
                NSManagedObject *majorType = [self.types objectAtIndex:majorTypeRow];
                NSSet *minorTypes = [majorType valueForKey:@"details"];
                NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:@"order" ascending:NO];
                NSArray *sortDescriptors = [NSArray arrayWithObjects:sd, nil];
                NSArray *array = [minorTypes sortedArrayUsingDescriptors:sortDescriptors];
                [self.detailTypes setObject:array forKey:majorTypeName];
                [sd release];
                return [array count] + 1;
        }
    }
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
    NSUInteger majorTypeRow = [self.typePicker selectedRowInComponent:0] - 1;
    row--;
    if (pickerView == self.typePicker) {
        if (component == 0)
            [pickerView reloadComponent:1]; // I believe this is where the bug starts, but I can't find the exact line of code that causes the crash
        else {
            if (row == -1)
                self._detailType = nil;
            else {
                NSString *majorTypeName = [[self.types objectAtIndex:majorTypeRow] name];
                NSArray *dt = [self.detailTypes objectForKey:majorTypeName];
                if (dt)
                    self._detailType = [dt objectAtIndex:row];
            }
        }
    }
    NSIndexPath *path = [self.tableView indexPathForSelectedRow];
    [self.tableView reloadData];
    [self.tableView selectRowAtIndexPath:path animated:YES scrollPosition:UITableViewScrollPositionNone];
}
4

2 回答 2

7

崩溃是由于您的代表中的这条线

NSUInteger majorTypeRow = [self.typePicker selectedRowInComponent:0] - 1;

正如其他人指出的那样,您需要保留两个单独的数组或对象,它们可以为各个组件提供数据源。

你有一个majorType 和detailType。majorTypes你可以有一个和的数组selectedMajorType。保留一个额外的 selectedMajorType 消除了使用的需要,selectedRowInComponent:并且不再使应用程序崩溃。

#pragma mark - UIPickerViewDataSource

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

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    NSInteger rows = 0;
    if (component) {
         rows = [self.selectedMajorType.minorTypes count];
    }else{
        rows = [self.majorTypes count];
    }
    return rows+1;
}

UIPickerViewDelegate 实现

- (NSString *)pickerView:(UIPickerView *)pickerView
             titleForRow:(NSInteger)row
            forComponent:(NSInteger)component
{
    row--;
    NSString *title = @"None";
    if (component) {
        NSArray *minorTypes = [self.selectedMajorType.minorTypes allObjects];
        if (row >= 0 && row < [minorTypes count]) {
            MinorType *minorType = minorTypes[row];
            title = minorType.name;
        }
    }else{
        if(row>=0 && row < [self.majorTypes count]){
            MajorType *majorType = self.majorTypes[row];
            title =  majorType.name;
        }
    }
    return title;
}

- (void)pickerView:(UIPickerView *)pickerView
      didSelectRow:(NSInteger)row
       inComponent:(NSInteger)component
{
    row--;
    if (component==0) {
        if (row >= 0 && row < [self.majorTypes count]) {
             self.selectedMajorType = self.majorTypes[row];
        }else {
           self.selectedMajorType = nil;
        }

        [pickerView reloadComponent:1];
    }
}

演示源代码

于 2013-04-28T07:02:53.873 回答
1

好的,我想我理解了这个问题:

一起滚动两个组件的问题在于,由于您使用数据关系创建的依赖关系,您的 UIPickerView 数据源可能会发生变化。我认为您需要创建两个分别代表左右的数组,并根据您更改的组件重新填充!

可以通过在此处查看类似问题来找到解决方案!滚动两个组件时,具有两个组件的 UIPickerView 崩溃

此外,关于检查您的 UIPickerView 是否正在滚动,您可以简单地设置一个计时器。这里有一个有用的链接:确定 UIPickerWheel 是否正在滚动!通过检查当前行是否匹配,您可以(在很短的时间间隔内)判断用户是否移动了轮子!

我希望这是清楚和有帮助的!

于 2013-04-23T21:58:03.727 回答