Сбой Obj-c — App, если раздел tableview пуст?

#ios #objective-c #uitableview

#iOS #objective-c #uitableview

Вопрос:

Я использую приведенный ниже код для упорядочивания моего TableView в алфавитном порядке. Фильтр работает корректно, однако, если один из разделов не содержит результатов (т. Е. Для буквы «A» ничего не возвращается), это приводит к сбою моего приложения, поскольку TableView пытается заполнить ячейку данными, которых там нет. Есть идеи, как я могу это предотвратить?

ViewController.m

 - (void)viewDidLoad {
                [super viewDidLoad];
                
 self.clientSections = [NSArray arrayWithObjects:@"#", @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h", @"i", @"j", @"k", @"l", @"m", @"n", @"o", @"p", @"q", @"r", @"s", @"t", @"u", @"v", @"w", @"x", @"y", @"z", nil];
    
    
 NSMutableDictionary *viewParams1 = [NSMutableDictionary new];
        [viewParams1 setValue:@"cdata" forKey:@"view_name"];
        [DIOSView viewGet:viewParams1 success:^(AFHTTPRequestOperation *operation, id responseObject) {
            
            self.clients = [responseObject mutableCopy];
          
            
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"last name" ascending:YES];
    
self.clientsAZ = [self.clients sortedArrayUsingDescriptors:@[sort]];
            
    
               } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"Failure: %@", [error localizedDescription]);
        }];
        
            
           -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    
        
        return [self.clientSections count];
        
    }
    
    - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
          return self.clientSections;
    }
    
    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
     
        return [self.clientSections objectAtIndex:section];
      
        
    }
            
            
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
            
                
 self.finalfiltered = [self.clientsAZ  filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(%K beginswith[cd] %@)", @"last name", [self.clientSections objectAtIndex:section]]];
                    
                
                    return [self.finalfiltered count];
                 
            }
            
    
            -(UITableViewCell *)tableView:(UITableView*)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
                
               
                    static NSString *ClientTableIdentifier = @"ClientTableViewCell";
                    
                    ClientTableViewCell *cell = (ClientTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:ClientTableIdentifier];
                    
                    if (cell == nil)
                    {
                        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"ClientTableViewCell" owner:self options:nil];
                        cell = [nib objectAtIndex:0];
                        
                    }
                   
                
                NSString *photo = [self.finalfiltered valueForKey:@"client photo"][indexPath.row];
                 
                NSString *first = [self.finalfiltered valueForKey:@"first name"][indexPath.row];
                   
                NSString *last = [self.finalfiltered valueForKey:@"last name"][indexPath.row];
                  
                NSString *telephone = [self.finalfiltered valueForKey:@"phone"][indexPath.row];
                  
                NSString *fullName = [NSString stringWithFormat:@"%@ %@", first, last];
              
                cell.clientName.text = fullName;
                cell.subtext.text = telephone;
                    
                NSURL *imageUrl = [NSURL URLWithString:photo];
                NSLog(@"The photo url is %@", photo);
                [cell.clientPhoto setImageWithURL:imageUrl];
                    
                    return cell;
                    
                    
                    }
                    
                    
              }
 

Вот что возвращается, когда я регистрирую self.finalfiltered. Edit: Обратите внимание, я сортирую по фамилии. Порядок кажется правильным.

 2021-02-10 16:53:34.905601-0800 [1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.905846-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.906076-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.906335-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.906566-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.906772-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.907025-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.907558-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.907772-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.908296-0800 -[1784:532468] THE FINAL FILTER (
        {
        body = none;
        city = Victoria;
        "client photo" = "/stockphotos/person5.png";
        country = Canada;
        email = "thomas.ingram@outlook.com";
        "first name" = Thomas;
        "last name" = Ingram;
        "mailing address" = "502 Catherine Street";
        "mailing address 2" = "Apt 403";
        nid = 127;
        "node_title" = "Thomas Ingram";
        notes = "test notes";
        phone = "250-998-0389";
        scheduleddate = "Feb 8 2021 1:00 PM";
        scheduledtime = none;

        "state or province" = BC;
        "zip code" = "V9A 3T3";
    }
)
2021-02-10 16:53:34.908833-0800 -[1784:532468] THE FINAL FILTER (
        {
        body = none;
        city = Toronto;
        "client photo" = "/stockphotos/person2.png";
        country = Canada;
        email = "andrea@gmail.com";
        "first name" = Andrea;
        "last name" = Johnson;
        "mailing address" = "227 Willow Avenue";
        "mailing address 2" = "Unit 2034";
        nid = 124;
        "node_title" = "Andrea Johnson";
        notes = "test notes";
        phone = "416-223-2397";
        scheduleddate = "Feb 8 2021 07:00 PM";
        scheduledtime = "5:45 PM";
        "state or province" = ON;
        "zip code" = "M5M 1W4";
    }
)
2021-02-10 16:53:34.909541-0800 -[1784:532468] THE FINAL FILTER (
        {
        body = none;
        city = Burnaby;
        "client photo" = "/sites/default/files/stored/1612926759.jpg";
        country = Canada;
        email = email@email.com;
        "first name" = Cody;
        "last name" = Lin;
        "mailing address" = "4036 Pandora Street";
        "mailing address 2" = "-";
        nid = 171;
        "node_title" = "Cody Lin”;
        notes = "test notes”;
        phone = "604-250-7422";
        scheduleddate = none;
        scheduledtime = none;
        "state or province" = BC;
        "zip code" = V5C2A9;
    }
)
2021-02-10 16:53:34.910439-0800 -[1784:532468] THE FINAL FILTER (
        {
        body = none;
        city = "New York City";
        "client photo" = "/stockphotos/person4.png";
        country = US;
        email = "mlevy39@gmail.com";
        "first name" = Michael;
        "last name" = Levy;
        "mailing address" = "22 Lexington Avenue";
        "mailing address 2" = "Apt 102";
        nid = 126;
        "node_title" = "Michael Levy";
        notes = "test notes";
        phone = "212-983-0029";
        scheduleddate = "Feb 10 2021 1:00 PM";
        scheduledtime = none;
        "state or province" = NY;
        "zip code" = 90020;
    },
        {
        body = none;
        city = London;
        "client photo" = "/stockphotos/person1.png";
        country = Canada;
        email = "janinejlohr@gmail.com";
        "first name" = Janine;
        "last name" = Monroe;
        "mailing address" = "909 Fake Street";
        "mailing address 2" = "Unit 4103";
        nid = 123;
        "node_title" = "Janine Monroe";
        notes = "test notes";
        phone = "778-028-2938";
        scheduleddate = "Feb 14 2021 7:37 PM, Feb 17 2021 9:00 AM";
        scheduledtime = none;
        "state or province" = BC;
        "zip code" = "V6E 4V2";
    }
)
2021-02-10 16:53:34.911207-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.911423-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.911630-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.911834-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.912033-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.912233-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.912431-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.913457-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.913671-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.913931-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.914378-0800 -[1784:532468] THE FINAL FILTER (
        {
        body = none;
        city = Vancouver;
        "client photo" = "/stockphotos/person3.png";
        country = Canada;
        email = "cali.wright@gmail.com";
        "first name" = Cali;
        "last name" = Wright;
        "mailing address" = "667 Fake Street";
        "mailing address 2" = "Apt 4102";
        nid = 125;
        "node_title" = "Cali Wright";
        notes = "test notes”;
        phone = "778-867-7184";
        scheduleddate = none;
        scheduledtime = none;
        "state or province" = BC;
        "zip code" = none;
    }
)
2021-02-10 16:53:34.914652-0800 -[1784:532468] THE FINAL FILTER (
)
2021-02-10 16:53:34.914868-0800 -[1784:532468] THE FINAL FILTER (
)
 

Возникающий сбой:

 Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
 

И, наконец, в коде он, похоже, падает прямо перед этой строкой:

  NSString *photo = [self.finalfiltered valueForKey:@"client photo"][indexPath.row];
 

Комментарии:

1. Вы знаете, на какой строке он выходит из строя?

2. Буквально прямо перед: NSString *photo = [self.finalfiltered valueForKey:@»client photo»][indexPath.row]; @jnpdx И когда я отключаю заполнение данных ячейки и просто регистрирую self.finalfiltered, организованный массив заполняется так, как и должно быть.

3. Можете ли вы изменить свой вопрос, указав, что указано в журнале, finalfiltered и в какой строке он выходит из строя (т. Е. То, Что говорит отладчик indexPath.row во время сбоя)?

4. Смотрите обновленное выше @jnpdx!

5. Я не собираюсь оставлять «ответ», чтобы вопрос оставался открытым и привлекал больше внимания, но ошибка очень четко показывает, что происходит — массив [self.finalfiltered valueForKey:@"client photo"] пуст, но вы просите его вернуть что-то для строки 0 . Это означает, что существует несоответствие между вашим кодом numberOfRows... и фактическим количеством строк, которые у вас есть в одном из массивов. Кроме того, меня беспокоит установка свойства, подобного finalfiltered in numberOfRows... , на случай, если система запрашивает что-то не по порядку, что может происходить здесь.

Ответ №1:

Ваш подход заключается в том, чтобы всегда возвращать 27 количество разделов в вашем tableview, соответствующих цифрам и буквам A-Z. Однако в a UITableView не может быть раздела с 0 строками. Это означает, что у вас проблема, когда ваши данные не содержат фамилии, начинающейся с «b», например; Вы возвращаете 0 для раздела 1, но это не разрешено. Tableview по-прежнему запрашивает строку 0 для пустого раздела, и вы получаете исключение границ массива.

Для целей ответа я немного упростил код отображения вашей ячейки, поскольку я не хотел полностью заполнять все поля данных и настраивать пользовательскую ячейку (вам действительно следует использовать объект для инкапсуляции вашей модели данных, а не словаря).

Большая часть работы выполняется в методе splitClients — это создает соответствующие массивы для каждого раздела и заголовков разделов. В этом коде я гарантировал, что данные были созданы в отсортированном порядке, но вы можете сортировать данные так, как вы делаете в своем коде.

Создавая правильные массивы, вы можете увидеть, насколько они проще numberOfSections и numberOfRowsInSection эффективнее — вы хотите, чтобы эти функции cellForRowAt были эффективными, поскольку они будут вызываться много раз по мере прокрутки представления таблицы.

Другой способ отметить sectionForSectionIndexTitle — поскольку не все разделы индекса могут быть заполнены, эта функция используется для возврата ближайшего заполненного раздела, поэтому, если вы выберете «b», а «b» отсутствуют, он будет прокручиваться до «c».

 @interface ViewController ()

@property NSArray<NSDictionary *> *clients;
@property NSArray<NSArray *> *sectionClients;
@property NSArray<NSString *> *sectionLetters;
@property NSArray<NSString *> *sectionHeaders;
@property IBOutlet UITableView *tableView;
@end

@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];
    self.sectionLetters = [NSArray arrayWithObjects:@"#", @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h", @"i", @"j", @"k", @"l", @"m", @"n", @"o", @"p", @"q", @"r", @"s", @"t", @"u", @"v", @"w", @"x", @"y", @"z", nil];
    // Do any additional setup after loading the view.
    
    NSMutableArray *clients = [NSMutableArray new];
    [clients addObject:[self clientWithFirstname:@"Andrew" lastName:@"67Anderson"]];
    [clients addObject:[self clientWithFirstname:@"Andrew" lastName:@"Anderson"]];
    [clients addObject:[self clientWithFirstname:@"Zaphod" lastName:@"Beeblebrox"]];
    [clients addObject:[self clientWithFirstname:@"Bob" lastName:@"Carlson"]];
    [clients addObject:[self clientWithFirstname:@"David" lastName:@"Carlson"]];
    [clients addObject:[self clientWithFirstname:@"Anthony" lastName:@"Edwards"]];
    [clients addObject:[self clientWithFirstname:@"Griff" lastName:@"Jones"]];
    [clients addObject:[self clientWithFirstname:@"Sara" lastName:@"Kelly"]];
    [clients addObject:[self clientWithFirstname:@"Mabel" lastName:@"Maloney"]];
    [clients addObject:[self clientWithFirstname:@"Horatio" lastName:@"Newton"]];
    [clients addObject:[self clientWithFirstname:@"Josie" lastName:@"Peters"]];
    [clients addObject:[self clientWithFirstname:@"Mel" lastName:@"Smith"]];
    [clients addObject:[self clientWithFirstname:@"Michael" lastName:@"Taylor"]];
    [clients addObject:[self clientWithFirstname:@"Mary" lastName:@"Zax"]];
    
    self.clients = [clients copy];
    
    [self splitClients];
    NSLog(@"Done splitting");
}

-(NSDictionary *)clientWithFirstname:(NSString *)firstName lastName:(NSString *)lastName {
    NSMutableDictionary *dict = [NSMutableDictionary new];
    dict[@"first name"] = firstName;
    dict[@"last name"] = lastName;
    return dict;
}

-(void)splitClients {
    
    NSMutableArray *sectionClients = [NSMutableArray new];
    NSMutableArray *sectionHeaders = [NSMutableArray new];
    
    // Look for last names that start with a digit using a regex

    NSArray *digitClients = [self.clients filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(%K MATCHES %@)",@"last name",@"[0-9].*" ]];
    
    // Add a section for digit names if any were found

    if ([digitClients count] > 0) {
        [sectionClients addObject:digitClients];
        [sectionHeaders addObject:@"#"];
    }
    
    // Now check for names starting with each letter of the alphabet

    for (NSString *letter in self.sectionLetters) {
        
        NSArray *letterArray = [self.clients filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(%K beginswith[cd] %@)", @"last name",letter]];

        // If any matching names were found, add them as a new section

        if ([letterArray count] >0) {
            [sectionClients addObject:letterArray];
            [sectionHeaders addObject:letter];
        }
    }
    
    self.sectionClients = [sectionClients copy];
    self.sectionHeaders = [sectionHeaders copy];
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [self.sectionClients count];
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return self.sectionLetters;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    
    return self.sectionHeaders[section];
 
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.sectionClients[section] count];
}

- (NSInteger)tableView:(UITableView *)tableView
sectionForSectionIndexTitle:(NSString *)title
               atIndex:(NSInteger)index {
    
    NSInteger targetSection = 0;
    
    for (NSString *letter in self.sectionHeaders) {
        NSComparisonResult result = [title compare:letter];
        // If the section index is >= the target index, break and exit
        if (result == NSOrderedAscending || result == NSOrderedSame) {
            break;
        }
        // Otherwise increment the section number
        targetSection  ;
    }
    
    return targetSection;
}


-(UITableViewCell *)tableView:(UITableView*)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
    
    NSDictionary *client = self.sectionClients[indexPath.section][indexPath.row];
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    
    cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", client[@"first name"], client[@"last name"]];
    
    return cell;
    
}

@end
 

Комментарии:

1. Вы … мастер. Это сработало так безупречно, что я хочу проголосовать за него 500000 раз. Большое вам спасибо.