簡體   English   中英

UITableView中的iOS 7 UITextView鏈接檢測崩潰

[英]iOS 7 UITextView link detection crash in UITableView

我在我的UITableView設置了一個自定義UITableView單元格,如下所示:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *identifier = @"CELL_IDENTIFIER";

    SGCustomCell *cell = (SGCustomCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) cell = [[SGCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];

    cell = [self customizedCell:cell withPost:[postsArray objectAtIndex:indexPath.row]];

    return cell;
}

我像這樣設置單元格(特別是將UITextView.text設置為nil - 如本答案中所述 ):

descriptionLabel.text = nil;
descriptionLabel.text = post.postDescription;

descriptionLabel.frame = CGRectMake(leftMargin - 4, currentTitleLabel.frame.origin.y + currentTitleLabel.frame.size.height + 10, self.frame.size.width - topMargin * 3, 100);
[descriptionLabel sizeToFit];

單元格是100%可重復使用的, UITextView就像這樣(你看,沒什么特別的):

descriptionLabel = [[UITextView alloc] init];
descriptionLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:11];
descriptionLabel.editable = NO;
descriptionLabel.scrollEnabled = NO;
descriptionLabel.dataDetectorTypes = UIDataDetectorTypeLink;
descriptionLabel.frame = CGRectMake(leftMargin, currentTitleLabel.frame.origin.y + currentTitleLabel.frame.size.height + 10, self.frame.size.width - topMargin * 3, 10);
[self addSubview:descriptionLabel];

但是當桌子有大約50個單元格時,當我快速滾動它我得到以下崩潰:

Terminating app due to uncaught exception 'NSRangeException', reason: 'NSMutableRLEArray objectAtIndex:effectiveRange:: Out of bounds'

這絕對是荒謬的 - 我注釋掉這一行 - descriptionLabel.dataDetectorTypes = UIDataDetectorTypeLink; 並且應用程序停止崩潰! 我花了好幾個小時試圖弄清問題是什么,現在我只是得到了這個。

在iOS 7.0.3上測試

當使用相同的單元標識符時,兩個具有數據類型的單元格正在出列時發生崩潰。 這似乎是iOS中的一個錯誤,但Apple可能有充分的理由以這種方式實現它。 (記憶明智)

因此,唯一的100%防彈解決方案是為包含數據類型的單元格提供唯一標識符。 這並不意味着您將為表中的所有單元格設置唯一標識符,當然,因為它會占用太多內存而您的表滾動會非常慢。

您可以使用NSDataDetector確定是否在文本中找到匹配的類型,然后將找到的對象保存為單元格標識符,如下所示:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    NSString *row = [self.dataSource objectAtIndex:indexPath.row];
    static NSDataDetector *detector = nil;
    if (!detector)
    {
        NSError *error = NULL;
        detector = [[NSDataDetector alloc] initWithTypes:NSTextCheckingTypeLink | NSTextCheckingTypePhoneNumber error:&error];
    }

    NSTextCheckingResult *firstDataType = [detector firstMatchInString:row
                                                               options:0
                                                                 range:NSMakeRange(0, [row length])];
    NSString *dataTypeIdentifier = @"0";
    if (firstDataType)
    {
        if (firstDataType.resultType == NSTextCheckingTypeLink)
            dataTypeIdentifier = [(NSURL *)[firstDataType URL] absoluteString];
        else if (firstDataType.resultType == NSTextCheckingTypePhoneNumber)
            dataTypeIdentifier = [firstDataType phoneNumber];
    }

    NSString *CellIdentifier = [NSString stringWithFormat:@"Cell_%@", dataTypeIdentifier];

    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
...

注意:將NSDataDetector *檢測器初始化為靜態而不是為每個單元初始化它可以提高性能。

我可以重現你的崩潰。 在TableViewCell子類中實現以下方法

- (void)prepareForReuse
{
    [super prepareForReuse];
    [descriptionLabel setDataDetectorTypes: UIDataDetectorTypeNone];
}

並在以下內容中添加以下調用- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath在設置文本之前:

[descriptionLabel setDataDetectorTypes: UIDataDetectorTypeLink];

為我工作。 也許它取消了在textview中持續繪制並避免崩潰的方式。

編輯:調用[descriptionLabel setDataDetectorTypes: UIDataDetectorTypeNone]; [descriptionLabel setDataDetectorTypes: UIDataDetectorTypeLink]; 在設置文本之前,似乎也解決了崩潰問題

如果您使用的是iOS6或更高版本,則可以使用NSDataDetector創建一個可歸屬的字符串,並將其用作TextView文本。 我們將要使用以下方法的修改版本。 該方法采用字符串和一些已預定義的屬性(如字體和文本顏色),並在第100個鏈接后停止。 但是,它有多個電話號碼的問題。 您需要為URL轉發地址定義自己的代碼。 NSDataDetector位取自Apple的NSDataDetector參考: https//developer.apple.com/librarY/mac/documentation/Foundation/Reference/NSDataDetector_Class/Reference/Reference.html

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string attributes:attributes];
__block NSUInteger count = 0;
if (!_dataDetector)
{
    NSError *error = nil;
    _dataDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeAddress | NSTextCheckingTypePhoneNumber | NSTextCheckingTypeLink
                                                    error:&error];
}
[_dataDetector enumerateMatchesInString:string
                                options:0
                                  range:NSMakeRange(0, [string length])
                             usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
                                 NSRange matchRange = [match range];
                                 if ([match resultType] == NSTextCheckingTypeLink)
                                 {
                                     NSURL *url = [match URL];
                                     if (url)
                                     {
                                         [attributedString addAttribute:NSLinkAttributeName value:url range:matchRange];
                                     }
                                 }
                                 else if ([match resultType] == NSTextCheckingTypePhoneNumber)
                                 {
                                     NSString *phoneNumber = [NSString stringWithFormat:@"tel:%@",[match phoneNumber]];
                                     NSURL *url = [NSURL URLWithString:phoneNumber];
                                     if (url)
                                     {
                                         [attributedString addAttribute:NSLinkAttributeName value:url range:matchRange];
                                     }
                                 }
                                 else if ([match resultType] == NSTextCheckingTypeAddress)
                                 {
                 //Warning! You must URL escape this!
                                     NSString *address = [string substringWithRange:matchRange];
                 //Warning! You must URL escape this!

                                     NSString *urlString = [NSString stringWithFormat:@"http://maps.apple.com/?q=%@",address];
                                     NSURL *url = [NSURL URLWithString:urlString];
                                     if (url)
                                     {
                                         [attributedString addAttribute:NSLinkAttributeName value:url range:matchRange];
                                     }
                                 }
                                 if (++count >= 100) *stop = YES;
                             }];
return attributedString;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM