Создание перехода программно


202

У меня есть общее, UIViewControllerчто все мои возможности UIViewsControllersраспространяются на повторное использование некоторых общих операций.

Я хочу настроить переход на этом «общем», UIViewControllerчтобы все остальные UIViewControllersнаследовали.

Я пытаюсь понять, как мне это сделать программно.

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

Ответы:


169

По определению, сегмент не может существовать независимо от раскадровки. Это даже там, во имя класса UIStoryboardSegue. Вы не создаете сегменты программно - это среда раскадровки, которая создает их для вас. Обычно вы можете вызывать performSegueWithIdentifier:код контроллера представления, но это зависит от того, будет ли уже настроен переход в раскадровке для ссылки.

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

- (IBAction)pushMyNewViewController
{
    MyNewViewController *myNewVC = [[MyNewViewController alloc] init];

    // do any setup you need for myNewVC

    [self presentModalViewController:myNewVC animated:YES];
}

и затем в своем производном классе вызовите этот метод, когда нажата соответствующая кнопка или выбрана строка таблицы или что-то еще.


4
Спасибо. Жаль, что мы не можем сделать это программно. Это действительно повысит качество исходного кода (меньше дублирования всегда хорошо). Я пойду с вашим предложением.
Tiago Veloso

2
@jonkroll можно ли вызвать / выполнить segue из оператора switch, т.е. основываясь на том, какой у меня индекс?
codejunkie

5
@codejunkie: Да, вы можете сделать это. Вы бы использовали UIViewControllerметод, названный performSegueWithIdentifier:sender:для этого.
jonkroll

2
Я создавал и выполнял segue программно (см. Мой ответ). Что-то не так с моим кодом, тогда, если ваш ответ правильный?
Жан-Филипп Пелле

14
Обновление для IOS 6+: UIView«s presentModalViewController:animated:является устаревшим. Из документов - (Устаревший в iOS 6.0. Используйте presentViewController: animated: завершение: вместо.)
пользователь

346

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

введите описание изображения здесь

Появится всплывающее окно «Ручной переход». Я выбрал Push как тип. Нажмите на маленький квадрат и убедитесь, что вы находитесь в инспекторе атрибутов. Дайте ему идентификатор, который вы будете использовать для ссылки на него в коде.

введите описание изображения здесь

Хорошо, теперь я собираюсь перейти с помощью кнопки панели инструментов. В viewDidLoad или в другом месте я создам элемент кнопки на панели навигации с таким кодом:

UIBarButtonItem *buttonizeButton = [[UIBarButtonItem alloc] initWithTitle:@"Buttonize"
                                                                    style:UIBarButtonItemStyleDone
                                                                   target:self
                                                                   action:@selector(buttonizeButtonTap:)];
self.navigationItem.rightBarButtonItems = @[buttonizeButton];

Хорошо, обратите внимание, что селектор - buttonizeButtonTap :. Поэтому напишите метод void для этой кнопки, и внутри этого метода вы будете вызывать segue следующим образом:

-(void)buttonizeButtonTap:(id)sender{
    [self performSegueWithIdentifier:@"Associate" sender:sender];
    }

Параметр sender необходим для идентификации кнопки при вызове prepareForSegue. prepareForSegue - это метод фреймворка, в котором вы создадите экземпляр своей сцены и передадите ей все значения, которые понадобятся для ее работы. Вот как выглядит мой метод:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"Associate"])
    {
        TranslationQuizAssociateVC *translationQuizAssociateVC = [segue destinationViewController];
        translationQuizAssociateVC.nodeID = self.nodeID; //--pass nodeID from ViewNodeViewController
        translationQuizAssociateVC.contentID = self.contentID;
        translationQuizAssociateVC.index = self.index;
        translationQuizAssociateVC.content = self.content;
    }
}

Хорошо, только что проверил, и это работает. Надеюсь, это поможет вам.


@MichaelRowe, как это устраняет необходимость в переходах? Насколько я понимаю, вам все еще нужно перетаскивать Storyboard к контроллеру назначения ..
aherrick

@MichaelRowe это не устраняет необходимость Segues. Это позволяет вам переключаться между контроллерами представления, которые встроены в код, а не в конструктор интерфейса.
Мэтью

@Matt это на самом деле просто пойти мне полностью переосмыслить, как я настраиваю свое приложение ... После полного переписывания всего пользовательского интерфейса я больше не использую segues ..
Майкл Роу

@cocoanut я получаю сообщение об ошибке, так как «Приложение пыталось представить модально активный контроллер» любая помощь по этому поводу ..
Бала

1
Руководство Segue "Push" устарело, используйте "Show". Этот ответ имеет больше деталей. @smileBot, пожалуйста, обновите ответ.
NAbbas

81

Я использовал этот код для создания своего собственного подкласса segue и запуска его программно. Вроде работает. Что-то не так с этим? Я озадачен, читая все остальные ответы о том, что это невозможно.

UIViewController *toViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"OtherViewControllerId"];
MyCustomSegue *segue = [[MyCustomSegue alloc] initWithIdentifier:@"" source:self destination:toViewController];
[self prepareForSegue:segue sender:sender];
[segue perform];

4
Что находится в MyCustomSegue?
Виктор Энгель

3
Это пользовательский подкласс UIStoryboardSegue.
Жан-Филипп Пелле

7
@MarkAmery Многие люди (включая меня) избегают использования раскадровок. Их трудно объединить, и нет никакой проверки во время компиляции, что ID, который я передаю, performSegueWithIdentifier:действительно определен в раскадровке. Я избегаю всех проблем, если я создаю Segue самостоятельно.
Жан-Филипп Пелле

3
Я согласен с Жан-Филиппом. Управление раскадровкой - это боль в заднице. Конечно, легко проложить свой путь, составив несколько представлений и добавив здесь переход и переход там, но управлять 6 представлениями с 16 определенными в XML сегментами, когда у вас есть три разработчика, все возиться с этим ужасно. В любом случае, дело в том, что код дает вам контроль, а xml, сгенерированный xcode, - нет.
Кристиан

3
Я вижу сбой в [segue execute] в iOS7, не уверен, что кто-то еще испытывает это.
Эрик Чен

45

Полагаю, что на это ответили и приняли, но я просто хотел бы добавить еще несколько деталей.

Что я сделал, чтобы решить проблему, когда я представлял вид входа в систему в качестве первого экрана, а затем хотел перейти к приложению, если вход был правильным. Я создал переход от контроллера входа в систему к корневому контроллеру представления и дал ему идентификатор типа «myidentifier».

Затем, проверив весь код входа в систему, если логин был правильным, я позвоню

[self performSegueWithIdentifier: @"myidentifier" sender: self];

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


4
Как я написал в качестве другого комментария: я создавал и выполнял пользовательские сегменты программно (см. Мой ответ).
Жан-Филипп Пелле

32

Вы должны связать свой код с тем, UIStoryboardкоторый вы используете. Убедитесь, что вы входите в YourViewController в вашем UIStoryboard, нажмите на границу вокруг него, а затем установите его identifierполе в то, NSStringчто вы вызываете в своем коде.

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
                                                     bundle:nil];
YourViewController *yourViewController = 
 (YourViewController *)
  [storyboard instantiateViewControllerWithIdentifier:@"yourViewControllerID"];
[self.navigationController pushViewController:yourViewController animated:YES];

1
Я получаю это, но что, если viewController, который я хочу представить, встроен в NavigationController в раскадровке? Из того, что я могу найти, я могу инициализировать NavigationController для встраивания его, но в раскадровке у меня уже есть настройка push segues для представления, которое необходимо представить.
jhilgert00

1
Вы можете остановиться на этом? Я думаю, что это проблема, которую я имею, но не могу найти, как / где это сделать ...
jesses.co.tt

1
Даже это решение правильное. Речь идет о том, чтобы избежать какой-либо сегрегации, но вопрос о секвенции. Таким образом, вы можете соединиться или сделать переход между двумя сценами БЕЗ перехода в раскадровке.
BootMaker

16

Для контроллеров, которые находятся в раскадровке.

jhilgert00 это то, что вы искали?

-(IBAction)nav_goHome:(id)sender {
UIViewController *myController = [self.storyboard instantiateViewControllerWithIdentifier:@"HomeController"];
[self.navigationController pushViewController: myController animated:YES];

}

ИЛИ...

[self performSegueWithIdentifier:@"loginMainSegue" sender:self];

3

ну, вы можете создать, а также можете создать подкласс UIStoryBoardSegue. Подклассы в основном используются для создания пользовательской анимации перехода.

Вы можете посмотреть видео с wwdc 2011, представляющего StoryBoard. его также доступны в YouTube.

http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIStoryboardSegue_Class/Reference/Reference.html#//apple_ref/occ/cl/UIStoryboardSegue


3

Я хотел бы добавить уточнение ...

Распространенное недоразумение, фактически то, которое у меня было некоторое время, заключается в том, что prepareForSegue:sender:метод раскадровки вызывается этим методом. Это не. Seguard будет работать независимо от того, реализовали ли выprepareForSegue:sender: метод для этого (отклоняющегося) контроллера представления.

Я узнал об этом из отличных лекций iTunesU Пола Хегарти . Мои извинения, но, к сожалению, не могу вспомнить, какую лекцию.

Если вы соединяете переход между двумя контроллерами представления в раскадровке, но не реализуете prepareForSegue:sender: метод, переход все равно перейдет к целевому контроллеру представления. Однако это приведет к тому, что контроллер представления не подготовлен.

Надеюсь это поможет.


3

Сегменты раскадровки не должны создаваться вне раскадровки. Вам нужно будет подключить его, несмотря на недостатки.

Справочник UIStoryboardSegue четко гласит:

Вы не создаете объекты segue напрямую. Вместо этого среда выполнения раскадровки создает их, когда она должна выполнить переход между двумя контроллерами представления. Вы по-прежнему можете инициировать переход программно, используя executeSegueWithIdentifier: sender: метод UIViewController, если хотите. Вы можете сделать это, чтобы инициировать переход из источника, который был добавлен программно и поэтому недоступен в Интерфейсном Разработчике.

Вы все еще можете программно сказать раскадровке представить контроллер представления с помощью segue, используя presentModalViewController:или pushViewController:animated:вызовы, но вам понадобится экземпляр раскадровки.

Вы можете вызвать UIStoryboardметод класса s, чтобы получить именованную раскадровку с пакетом nil для основного пакета.

storyboardWithName:bundle:


2

Во-первых, предположим, что у вас есть два разных представления в раскадровке, и вы хотите перемещаться с одного экрана на другой, поэтому выполните следующие действия:

1). Определите все ваши представления с помощью файла класса, а также идентификатора раскадровки в инспекторе идентичности.

2). Убедитесь, что вы добавили контроллер навигации к первому виду. Выберите его в раскадровке, а затем выберите «Редактор»> «Встроить»> «Контроллер навигации».

3). В свой первый класс импортируйте «secondClass.h»

#import "ViewController.h
#import "secondController.h"

4). Добавьте эту команду в IBAction, который должен выполнить переход

secondController *next=[self.storyboard instantiateViewControllerWithIdentifier:@"second"];
[self.navigationController pushViewController:next animated:YES];

5). @"second"класс контроллера secondview, идентификатор раскадровки.


self.storyboardдолжно быть:UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
masipcat

@masipcat и название платы Story могут зависеть от того, как вы настроили свой проект Xcode, у меня это был «Main.storyboard», поэтому я использовалstoryboardWithName:@"Main"
ammianus

@ sanket-chauhan, если ваш первый контроллер не встроен в Navigation Controller, вы также можете показать следующий вид, используя [self showDetailViewController:next sender:self];или[self showViewController:next sender:self];
ammianus

1

Я пересмотрел проект и сделал (-и) реализацию сегментов UIStoryboard с открытым исходным кодом: https://github.com/acoomans/Segway

С помощью этой библиотеки вы можете определять сегменты программно (без раскадровки).

Надеюсь, это может помочь.


0

На самом деле пара проблем:

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

без идентификатора

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

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


0

Вот пример кода для Creating a segue programmatically:

class ViewController: UIViewController {
    ...
    // 1. Define the Segue
    private var commonSegue: UIStoryboardSegue!
    ...
    override func viewDidLoad() {
        ...
        // 2. Initialize the Segue
        self.commonSegue = UIStoryboardSegue(identifier: "CommonSegue", source: ..., destination: ...) {
            self.commonSegue.source.showDetailViewController(self.commonSegue.destination, sender: self)
        }
        ...
    }
    ...
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // 4. Prepare to perform the Segue
        if self.commonSegue == segue {
            ...
        }
        ...
    }
    ...
    func actionFunction() {
        // 3. Perform the Segue
        self.prepare(for: self.commonSegue, sender: self)
        self.commonSegue.perform()
    }
    ...
}

Вы звоните self.prepare(for: self.commonSegue, sender: self)с вашего метода действия. Тогда какой смысл сравнивать if self.commonSegue == segue {...}в prepare(for:sender)методе?
Найем

@nayem: In prepare(for:sender:), вы можете настроить контроллер представления назначения до его отображения. Конечно, вы также можете сделать это в actionFunction.
jqgsninimo

@nayem: причина, по которой я это делаю, состоит в том, чтобы попытаться быть последовательным с другими сегментами.
jqgsninimo
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.