Необходимо создать настраиваемое представление, например ABPeoplePickerNavigationController

Я хочу создать настраиваемый вид, похожий на контакты в iPhone. Есть ли какой-нибудь учебник по созданию пользовательского представления, подобного ABPeoplePickerNavigationController?

Обратите внимание, что я не хочу открывать контроллер средства выбора людей по умолчанию, предоставляемый фреймворком AddressBookUI. Кроме того, я хочу привязать этот контроллер навигации к моему основному виду.

Для справки, что именно я хочу, вы можете сослаться на вкладку контактов Whatsapp на устройстве iOS.

РЕДАКТИРОВАТЬ: у меня уже есть список контактов и отображаются имя и фамилия человека в виде таблицы. Теперь я хочу создать индекс для алфавитов от A до Z, и при нажатии на этот индекс в табличном представлении следует прокрутить до этих контактов. Кроме того, как я могу реализовать функцию поиска, чтобы находить пользователя по его / ее имени или фамилии?


person Nirmit Dagly    schedule 17.04.2015    source источник
comment
Я добавил ссылку на хороший учебник по индексам, которым следил некоторое время назад, проверьте мою последнюю строчку в моем ответе   -  person Gil Sand    schedule 17.04.2015
comment
Я уже ссылался на эту ссылку, о которой вы упомянули, но я не понимаю, как получать данные динамически?   -  person Nirmit Dagly    schedule 17.04.2015


Ответы (2)


У меня есть то же самое в приложении, которое я сейчас создаю, и мы также черпали вдохновение в таких приложениях, как Whatsapp, когда дело касалось контактов.

Я использовал простое табличное представление с настраиваемыми ячейками для визуальных элементов, просто с именем и изображением.

Мой процесс был следующим:

  • Создайте объект contact в coredata (или другом постоянном способе хранения ваших данных)
  • Через ABAddressbook фреймворк вы можете просматривать все свои контакты и преобразовывать их в новые объекты контактов. Сохраните ссылку на свой ABPerson в своем Contact объекте, это позволит вам позже находить и обновлять свои контакты, просто используя ссылки. Если вы этого не сделаете, вам придется просматривать всех своих ABPerson каждый раз, когда вы хотите обновить свои контакты. Вы можете использовать ABPersons напрямую, но это было бы очень болезненно кодировать.

  • После того, как вы извлекли все свои контакты, обязательно сохраните контекст, если вы используете основные данные, или сохраните их в .sqlite.

  • Затем вы можете просто извлечь их в массив контактов и отобразить в произвольной ячейке по вашему выбору.

Это руководство по appcoda представляет собой достойную настраиваемую ячейку для учебника по tableview. Вы можете найти еще тысячу, просто набрав в Google "tableview custom cell ios" и найдя разные вещи, которые могут вам понравиться. В конце концов, у вас будет просто ячейка с меткой и изображением, вы МОЖЕТЕ использовать простой UITableViewCell, который я использовал для другого табличного представления типа «контакт».

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

Я мог бы поделиться своим классом Contact, но он включает в себя много нерелевантного кода, который может вас запутать, потому что: - Я также проверяю, являются ли эти контакты уже пользователями моего приложения, чтобы переместить их в определенные разделы моего представления таблицы - Я разделил свои tableview в 27 разделах (пользователи, затем буквы алфавита).

Кроме того, и я остановлюсь на последнем общем совете по программированию: было бы неплохо сначала записать, что именно вам нужно и что вам нужно, изложить все возможности на бумаге и т. Д. Я наткнулся на много простых проблем, на решение которых мне потребовалось время, либо потому, что я не планировал должным образом, либо потому, что это было скрыто.

Например :

  • У некоторых из ваших контактов вообще нет имен.
  • У некоторых из ваших контактов есть имя в поле "Должность" (где вы пишете Доктор или Мистер)
  • У некоторых из ваших контактов нет номеров телефонов (если вы используете номера телефонов в качестве идентификаторов)
  • Некоторые из ваших контактов имеют международное форматирование, а некоторые нет (+32 46556555 или 0032 46556555)
  • У некоторых из ваших контактов есть фотографии, сделанные камерой, у других - из Gmail, которые имеют разные форматы.
  • У вас могут быть повторяющиеся контакты (одно и то же имя, все одинаково) из-за плохой синхронизации со стороны пользователя
  • Вы должны убедиться, что имя / фамилия находится в правильном разделе, кодирование с учетом регистра может вызвать проблемы.
  • Вам нужно найти решение для контакта, который начинается со смайлика или не буквенно-цифровых символов.
  • Вашим пользователям нужен индексный список сбоку
  • Конечно, вам нужно добавить панель поиска, потому что у некоторых людей более 1000 контактов.
  • Я гарантирую, что впереди еще много других.

Поскольку это очень специфично для приложения, я не собираюсь останавливаться на всех проблемах, которые у меня были, и о том, что я для них сделал, но вы поняли идею :) Не стесняйтесь задавать любые очень конкретные вопросы, и у меня уже может быть очень конкретный решение, так как мне в значительной степени пришлось скопировать контакты WhatsApp с нуля, и, черт возьми, я это сделал. (На самом деле я получил то же самое, что и Anonymess и iOS)

РЕДАКТИРОВАТЬ: Вот некоторые методы моих методов извлечения ABPerson; ContactDAO в основном взаимодействует с моей постоянной моделью (CoreData), и я считаю, что их названия достаточно ясны, чтобы вы могли понять, что происходит. Я доволен комментариями и именами переменных, так что вы должны прочитать их без особых проблем.

А вот и огромный блок кода.

- (NSMutableArray *)getAllRecordsWithPeople:(CFArrayRef)allPeople andAddressBook:(ABAddressBookRef)addressbook{
    NSMutableArray *newRecords = [[NSMutableArray alloc]init];
    CFIndex nPeople = ABAddressBookGetPersonCount(addressbook);

    for (int i=0;i < nPeople;i++){

        ABRecordRef ref = CFArrayGetValueAtIndex(allPeople,i);
        ABRecordID refId = ABRecordGetRecordID(ref);
        NSNumber *recId = [NSNumber numberWithInt:refId];
        [newRecords addObject:recId];
    }

    return newRecords;
}

- (void)getValidContactsFromAddressBookWithCompletionBlock:(void (^)(NSError *error))completion{

    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);

    __block BOOL accessGranted = NO;

    if (&ABAddressBookRequestAccessWithCompletion != NULL) {
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
            accessGranted = granted;
            dispatch_semaphore_signal(semaphore);
        });

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }

    if (accessGranted) {

        NSMutableArray *newRecords       = [[NSMutableArray alloc]init];
        NSMutableArray *updatedRecords   = [[NSMutableArray alloc]init];
        NSMutableArray *unchangedRecords = [[NSMutableArray alloc]init];

        CFArrayRef allPeople  = ABAddressBookCopyArrayOfAllPeople(addressBook);
        CFIndex nPeople       = ABAddressBookGetPersonCount(addressBook);

        //Checking the last time we updated
        NSTimeInterval lastSyncTime;
        if ([[NSUserDefaults standardUserDefaults]objectForKey:@"LastSyncTime"] == nil){
            //This is the first time we update.
            lastSyncTime = 0;
        }else{
            //Setting the last update in variable
            lastSyncTime = [[[NSUserDefaults standardUserDefaults]objectForKey:@"LastSyncTime"]doubleValue];
        }

        if (lastSyncTime == 0){
            //We have to insert everyone, this is the first time we do this.
            newRecords = [self getAllRecordsWithPeople:allPeople andAddressBook:addressBook];
        }else{
            //We have to manually compare everyone to see if something has changed.

            for (int i=0;i < nPeople;i++) {

                ABRecordRef ref = CFArrayGetValueAtIndex(allPeople,i);
                ABRecordID refId = ABRecordGetRecordID(ref);
                NSNumber *recId = @(refId);

                CFDateRef recordCreation = ABRecordCopyValue(ref, kABPersonCreationDateProperty);
                NSDate *recCreDate = (__bridge NSDate *)(recordCreation);
                NSTimeInterval creDateInterval = [recCreDate timeIntervalSince1970];

                if(creDateInterval > lastSyncTime){
                    //This object was created after my lastSync, this must be a new record
                    [newRecords addObject:recId];

                }else{

                    //Checking the last update of the given record
                    CFDateRef recordUpdate = ABRecordCopyValue(ref, kABPersonModificationDateProperty);
                    NSDate *recUpDate = (__bridge NSDate*)(recordUpdate);

                    if ([recUpDate timeIntervalSince1970] > lastSyncTime){
                        //The record was somehow updated since last time, we'll update it
                        [updatedRecords addObject:recId];

                    }else{
                        //The record wasn't updated nor created, it is therefore unchanged.
                        //We still need to keep it in a separate array to compare deleted contacts
                        [unchangedRecords addObject:recId];
                    }
                }

            }
            if(allPeople)
                CFRelease(allPeople);
        }

        [self manageNewContacts:newRecords updatedContacts:updatedRecords andUnchangedContacts:unchangedRecords inAddressBook:addressBook andBlock:^(NSError *error) {
            completion(error);
        }];
    }else{
        NSError *error = [NSError errorWithDomain:@"ABAccess access forbidden" code:403 userInfo:nil];
        completion(error);
    }
}

- (void)manageNewContacts:(NSMutableArray*)newRecords updatedContacts:(NSMutableArray*)updatedRecords andUnchangedContacts:(NSMutableArray*)unchangedRecords inAddressBook:(ABAddressBookRef)addressbook andBlock:(void (^)(NSError *error))completion{

    AppDelegate *app = [UIApplication sharedApplication].delegate;
    NSManagedObjectContext *context = app.managedObjectContext;

    //Getting all the CoreData contacts IDs to have something to compare
    NSArray *coreDataContactsIds = [ContactDAO getAllContactIdsInManagedObjectContext:context];

    for (NSDictionary *rec in coreDataContactsIds){
        NSNumber *recId = rec[@"record_id"];
        if (![unchangedRecords containsObject:recId]){
            //The unchanged record doesn't exist locally

            if (![updatedRecords containsObject:recId]){
                //The updated record doesn't exist locally

                if (![newRecords containsObject:recId]){
                    //The new record doesn't exist locally
                    //That means the ongoing record has been deleted from the addressbook,
                    //we also have to delete it locally

                    [ContactDAO deleteContactWithID:recId inManagedObjectContext:context];

                }

            }
        }

    }

    for (NSNumber *recId in updatedRecords){
        ABRecordID recordID = (ABRecordID)recId.intValue;
        ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressbook, recordID);

        NSDictionary *personDict = [self getPersonDictionaryFromABRecordRef:person];
        if (personDict){
            [ContactDAO updateContactWithFirstName:personDict[@"firstName"] lastName:personDict[@"lastName"] compositeName:personDict[@"compositeName"] picture:personDict[@"picture"] phoneNumbers:personDict[@"phoneNumbers"] recordID:recId inManagedObjectContext:context];
        }
    }

    for (NSNumber *recId in newRecords){
        ABRecordID recordID = (ABRecordID)recId.intValue;
        ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressbook, recordID);

        NSDictionary *personDict = [self getPersonDictionaryFromABRecordRef:person];
        if (personDict){
            [ContactDAO createContactWithFirstName:personDict[@"firstName"] lastName:personDict[@"lastName"] compositeName:personDict[@"compositeName"] picture:personDict[@"picture"] phoneNumbers:personDict[@"phoneNumbers"] recordID:recId inManagedObjectContext:context];
        }

    }

    NSError *dbError;
    [context save:&dbError];

    NSTimeInterval lastSyncTime = [[NSDate date]timeIntervalSince1970];

    [[NSUserDefaults standardUserDefaults]setObject:@(lastSyncTime) forKey:@"LastSyncTime"];
    completion(dbError);
}


- (NSDictionary*)getPersonDictionaryFromABRecordRef:(ABRecordRef)person{

    //Get name
    NSString * firstName, *lastName;
    firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
    lastName  = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));

    firstName =  (firstName == nil) ? @"" : firstName;
    lastName  =  (lastName == nil)  ? @"" : lastName;
    NSString *compositeName;

    if ([firstName isEqualToString:@""] && [lastName isEqualToString:@""]){
        return nil;
    }

    if ([lastName isEqualToString:@""]){
        compositeName = [NSString stringWithFormat:@"%@", firstName];
    }
    if ([firstName isEqualToString:@""]){
        compositeName = [NSString stringWithFormat:@"%@", lastName];
    }
    if (![lastName isEqualToString:@""] && ![firstName isEqualToString:@""]){
        compositeName = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
    }

    //Get picture
    CFDataRef imageData = ABPersonCopyImageData(person);
    NSData *data = CFBridgingRelease(imageData);

    //Get phone numbers
    NSMutableSet *phoneNumbers = [[NSMutableSet alloc]init];

    ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
    for(CFIndex i = 0; i < ABMultiValueGetCount(phones); i++) {

        CFStringRef str = ABMultiValueCopyValueAtIndex(phones, i);
        NSString *num = CFBridgingRelease(str);
        [phoneNumbers addObject:num];
        /*if(str)
         CFRelease(str);*/
    }

    //Save it in dictionary
    NSDictionary *personDict = [[NSDictionary alloc]initWithObjectsAndKeys:firstName, @"firstName",lastName , @"lastName",phoneNumbers,@"phoneNumbers", compositeName, @"compositeName", data, @"picture", nil];

     //Release everything.
     if(phones)
     CFRelease(phones);

    return personDict;

}

Когда дело доходит до индексов, это руководство подойдет.

person Gil Sand    schedule 17.04.2015
comment
Спасибо за Ваш ответ. Я получил список контактов из телефонной книги и сохранил их в массиве словаря. Теперь вы можете мне помочь. Как я могу создать индексы для табличного представления и реализовать функцию поиска для поиска контактов по имени или фамилии? - person Nirmit Dagly; 17.04.2015
comment
это две совершенно разные вещи. Я настоятельно рекомендую вам просто использовать Google SearchBar tutorial ios, перейти по первым двум ссылкам и сделать то же самое с index. Там все объясняется изображениями и всем остальным. Просто следуйте их руководству, это займет у вас около 2 часов, а затем адаптируйте код к своему приложению. В какой-то момент мы не можем просто скопировать все в stackoverflow - person Gil Sand; 17.04.2015
comment
Я пройду через эти уроки и еще раз благодарю вас за помощь. Я понимаю, что мы не можем скопировать и вставить все при переполнении стека. Еще раз спасибо за вашу помощь. - person Nirmit Dagly; 17.04.2015
comment
Спасибо Зилу за то, что помог мне в этом .. !! - person Nirmit Dagly; 22.04.2015

Взгляните на это: http://www.appcoda.com/ios-programming-index-list-uitableview/

Дать желаемый результат помогают методы табличного просмотра:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView

и

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title 
person Kamal Bhardwaj    schedule 17.04.2015
comment
Спасибо за ваш комментарий и ссылку. Я уже знаю об этих методах, но хочу знать, как я могу создать индекс из списка контактов и реализовать поиск? - person Nirmit Dagly; 17.04.2015