Подтвердить что ты не робот

Правильное создание ручного перехода через раскадровку и xcode

Я новичок в xcode, и я пытаюсь разработать пример приложения, которое в основном представляет собой встроенное табличное представление, которое имеет много уровней. У меня есть plist, который хранит ячейки каждого вида таблицы. Если в ячейках нет дочерних элементов, то я хочу иметь возможность перейти к одному подробному виду после нажатия на ячейку. В конце концов я хочу иметь возможность перейти к различным подробным представлениям в зависимости от типа данных. Чтобы сделать это, я создал подробный вид из раскадровки, перетащил мой контроллер просмотра в свой подробный вид, чтобы создать руководство "Push" segue, и обозначил segue "segue1".

Изменить: Исходный код здесь

Building Manual Segue

Затем я заполняю то, что, как я предполагал, является необходимыми для этого функциями, заключается в вызове [self performSegueWithIdentifier:@"segue1" sender:myString];, где myString - это название ячейки, которую я выбрал.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //Check the dictionary to see what cell was clicked
    NSDictionary *dict = [self.tableDataSource objectAtIndex:indexPath.row];
    NSString *myString = [dict objectForKey:@"Title"];
    NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];

    NSArray *children = [dictionary objectForKey:@"Children"];

    //If there is no children, go to the detailed view
    if([children count] == 0)
    {
        [self performSegueWithIdentifier:@"segue1" sender:myString];

    }
    else{
        //Prepare to tableview.
        DrillDownViewController *rvController = [[DrillDownViewController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]];

        //Increment the Current View
        rvController.CurrentLevel += 1;

        //Set the title;
        rvController.CurrentTitle = [dictionary objectForKey:@"Title"];

        //Push the new table view on the stack
        [self.navigationController pushViewController:rvController animated:YES];

        rvController.tableDataSource = children;

    }

}

Наконец, я позвонил в сборник для segue, который ищет segue с надписью segue1.

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"segue1"])
    {
        DrillDownDetailController *dvController = [[segue destinationViewController] visibleViewController];
        //DrillDownDetailController *dvController = [[DrillDownDetailController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]];
        [dvController setItemName:(NSString *)sender];
        [self.navigationController pushViewController:dvController animated:YES];
    }
}

Я думал, что это сработает, но по какой-то причине, когда код достигает [self performSegueWithIdentifier:@"segue1" sender:myString];, он ломается с ошибкой

***** Завершение приложения из-за неперехваченного исключения "NSInvalidArgumentException", причина: "Receiver() не имеет сеанса с идентификатором" segue1 "' * Первый стек вызовов броска: (0x14b4022 0xeb4cd6 0xdf61b 0x3590 0xa85c5 0xa87fa 0x93d85d 0x1488936 0x14883d7 0x13eb790 0x13ead84 0x13eac9b 0x139d7d8 0x139d88a 0x17626 0x23ed 0x2355 0x1) terminate называется throwing exception (lldb)

Я не могу понять, почему он говорит мне, что не может найти segue1, когда он уже определен в раскадровке и коде.

4b9b3361

Ответ 1

Пара проблем:

Сначала, в том проекте, который вы загрузили для нас, segue не несет идентификатор "segue1":

no identifier

Вы должны заполнить этот идентификатор, если вы еще этого не сделали.

Второй, когда вы переходите из представления таблицы в таблицу, вы вызываете initWithNibName для создания контроллера вида. Вы действительно хотите использовать instantiateViewControllerWithIdentifier.

Таким образом, строка, которая гласит:

DrillDownViewController *rvController = [[DrillDownViewController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]];

должен сказать:

DrillDownViewController *rvController = [self.storyboard instantiateViewControllerWithIdentifier:@"TableView"];

Третий, ваш prepareForSegue был:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"segue1"])
    {
        dvController = [[segue destinationViewController] visibleViewController];
        [dvController setItemName:self->nameToSend];
    }
}

И должно быть упрощено исключение ссылки на visibleViewController, например:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"segue1"])
    {
        dvController = segue.destinationViewController;
        dvController.itemName = nameToSend;
    }
}

Четвертый, ваш DrillDownDetailController.m имеет этот метод:

-(void) setItemName:(NSString *)itemName
{
    if(!itemName)
    {
        itemName = [[NSString alloc] initWithString:itemName];
    }
    label.text = itemName;
}

Здесь есть куча проблем, но вы просто не должны обновлять label.text здесь (потому что он еще не создан!). Вы должны полностью исключить этот метод настраиваемого метода настройки (просто позвольте Xcode синтезировать стандартный наборщик для вас) и просто измените свой viewDidLoad следующим образом:

- (void)viewDidLoad
{
    [super viewDidLoad];

    label.text = self.itemName;
}

Убедитесь, что вы не обновляете объекты пользовательского интерфейса до viewDidLoad!

Я не прошел всю программу, но с этими четырьмя исправлениями я могу нажать "SubItem1", а затем "Автомобили", а затем "BMW", и я вижу подробный экран, в котором говорится "BMW". Я думаю, что есть некоторые проблемы с вашим plist на других предметах (например, записи в обуви - это строки, а не словарные записи, и вы получаете ошибку... Предполагаю, что вы просто не заполнили свой plist полностью), но вышеупомянутые исправления исправить более серьезные проблемы с кодированием.

Ответ 2

ОСНОВНОЙ ВОПРОС ЗДЕСЬ, вы создавали пустой DrillDownViewController, не повторно используя один из раскадровки, см. комментарии в коде ниже

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
        if([[segue identifier] isEqualToString:@"segue1"])
        {
             [segue.destinationViewController setItemName:(NSString*)sender];
        }
    }

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        //Check the dictionary to see what cell was clicked
       NSDictionary *dict = [self.tableDataSource objectAtIndex:indexPath.row];
NSString *titleName = [dict objectForKey:@"Title"];
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];

NSArray *children = [dictionary objectForKey:@"Children"];

if([children count] == 0)
{
    [self performSegueWithIdentifier:@"segue1" sender:titleName];

}
        else{
    //Prepare to tableview.

    //THE MAIN ISSUE IS HERE, you were creating a blank DrillDownViewController, not reusing the one from storyboards so it did not have a segue at all defined. instead do this:
    UIStoryboard*  sb = [UIStoryboard storyboardWithName:@"MainStoryboard"
                                                  bundle:nil];
    DrillDownViewController *rvController = [sb instantiateViewControllerWithIdentifier:@"DDVC"];       
    //Increment the Current View
    rvController.CurrentLevel += 1;

    //Set the title;
    rvController.CurrentTitle = [dictionary objectForKey:@"Title"];

    //Push the new table view on the stack
    [self.navigationController pushViewController:rvController animated:YES];

    rvController.tableDataSource = children;

}


    }