Как пропустить 2 представления при использовании контроллера навигации
В Xcode 5.0.2 я создал приложение для iPhone (полное приложение code на GitHub, запустите pod install
один раз - для SDWebImage
), в котором отображается список социальных сетей (Facebook, Google+ и т.д.), затем - в другом виде - UIWebView
, отображающий веб-страницу OAuth2, а затем третье представление с аватаром пользователя и информацией (имя, фамилия и т.д.):
![Xcode screenshot]()
(Здесь полноэкранный снимок экрана раскадровки, показанный выше)
![enter image description here]()
Прежде чем отображать аватар пользователя и данные в последнем представлении, я сохраняю данные пользователя в NSUserDefaults
.
Мой вопрос:, когда пользователь запускает приложение в следующий раз и уже есть данные пользователя в NSUserDefaults
- как я могу пропустить первые 2 представления и отобразить (без какой-либо анимации) последний вид с данными пользователя?
Я добавил следующий код в AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *key = [defaults objectForKey:@"key"];
User *user = [User loadForKey:key];
if (user) {
UINavigationController *nc = (UINavigationController*)self.window.rootViewController;
// XXX how to load the last view here? XXX
}
return YES;
}
и может видеть, что пользовательские данные загружаются в порядке, но я не понимаю, как перейти к последнему виду (особенно потому, что большая часть пользовательского интерфейса определена в раскадровке)?
ОБНОВЛЕНИЕ: Я пробовал следовать предложению Томми Александра (спасибо!) со следующим кодом в AppDelegate.m (а также присвоенный Details
StoryboardID до последнего представления):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *key = [defaults objectForKey:@"key"];
User *user = [User loadForKey:key];
if (user) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *uvc = [storyboard instantiateViewControllerWithIdentifier:@"Details"];
NSLog(@"XXX uvc=%@ user=%@", uvc, user);
[self.window.rootViewController presentViewController:uvc animated:YES completion:nil];
}
return YES;
}
и в то время как последний вид (UserViewController с аватаром пользователя и деталями), похоже, полностью создан, я получаю следующее предупреждение и приложение не переходит к последнему виду:
MyAuth[3917:70b] XXX uvc=<UserViewController: 0x8acbac0> user=
VK
59751265
Alexander
Farber
1945522
http://cs319319.vk.me/v319319265/b7df/TnyKeffL_mU.jpg
0
MyAuth[3917:70b] Warning: Attempt to present <UserViewController: 0x8acbac0> on <UINavigationController: 0x8bb3a30> whose view is not in the window hierarchy!
ОБНОВЛЕНИЕ 2:. Когда я перемещаю вышеуказанный код в viewDidLoad
первого вида (MasterViewController.m, где пользователь может выбрать социальную сеть), затем я получаю другое предупреждение:
MyAuth[1358:70b] XXX uvc=<UserViewController: 0x8a97430> user=
FB
597287941
Alexander
Farber
Bochum, Germany
http://graph.facebook.com/597287941/picture?type=large
0
MyAuth[1358:70b] Unbalanced calls to begin/end appearance transitions for <UINavigationController: 0x8a893e0>.
ОБНОВЛЕНИЕ 3: Спасибо за ценные ответы! Я в конечном итоге использовал предложение Леонтия Дериглазова:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *vc1 = [storyboard instantiateViewControllerWithIdentifier:@"Master"];
//UIViewController *vc2 = [storyboard instantiateViewControllerWithIdentifier:@"Login"];
UIViewController *vc3 = [storyboard instantiateViewControllerWithIdentifier:@"User"];
User *user = [User load];
NSArray *controllers = (user ? @[vc1, vc3] : @[vc1]);
UINavigationController *nc = (UINavigationController *)self.window.rootViewController;
[nc setViewControllers:controllers];
return YES;
}
Ответы
Ответ 1
Вкратце, наиболее простым решением является использование -[UINavigationController setViewControllers:animated:]
.
Для достижения желаемого результата необходимо выполнить следующие действия:
- присваивать идентификаторы раскадровки всем вашим контроллерам,
- создать экземпляр всех трех контроллеров представления из панели рассказов (это легкий вес, если вы не выполняете большую часть инициализации в конструкторах контроллера).
- помещаем их в массив в том же порядке, что и у вас в раскадровке,
- вызов
-[UINavigationController setViewControllers:]
с массивом в качестве первого аргумента.
Что это. Код в вашем -[AppDelegate application:didFinishLaunchingWithOptions:]
может выглядеть так (после проверки вашего условия прохода, конечно):
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *vc1 = [storyboard instantiateViewControllerWithIdentifier:@"MyAuth"]; //if you assigned this ID is storyboard
UIViewController *vc2 = [storyboard instantiateViewControllerWithIdentifier:@"Login"]; //if you assigned this ID is storyboard
UIViewController *vc3 = [storyboard instantiateViewControllerWithIdentifier:@"Details"];
NSArray *controllers = @[vc1, vc2, vc3];
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
[navController setViewControllers:controllers];
Если вы введете только это в -[AppDelegate application:didFinishLaunchingWithOptions:]
, вы увидите, что он работает немедленно.
Ответ 2
Искажение иерархии просмотров всегда дает странное поведение.
Используя нижеприведенный подход, пользователь не сможет заметить, что LoginViewController представлен, а затем мы нажали на экран Detail.
Этот подход всегда работал у меня
В файле LoginViewController.m
- (void)viewDidLoad
{
if (user) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UserViewController *userViewController = [storyboard instantiateViewControllerWithIdentifier:@"Details"];
[self.navigationController pushViewController:listViewVC animated:YES];
}
}
В файле UserViewController.m
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
if (user) {
self.navigationItem.hidesBackButton = YES;
}
}
Ответ 3
Я думаю, что ваша проблема начинается с вашей начальной настройки раскадровки. Я бы использовал что-то вроде изображения ниже.
Я бы сделал User View controller
мой контроллер корневого представления и представил контроллеры виджетов входа в моду с анимацией или без нее.
После успешного входа в систему будет довольно удобно отклонить контроллеры представления. И если пользователь выйдет позже, вам просто нужно будет снова представить контроллеры просмотра входа.
![enter image description here]()
Ответ 4
Я думаю, вы можете это сделать:
if (hasUser) {
MyFirstViewController *firstViewController = [myStoryboard instantiateViewControllerWithIdentifier:@"FirstViewControllerStoryboardId"];
SecondViewController *secondViewController = [myStoryboard instantiateViewControllerWithIdentifier:@"SecondViewControllerStoryboardId"];
[myNavigationController pushViewController:firstViewController animated:NO];
[myNavigationController pushViewController:secondViewController animated:YES];
}
Таким образом, у вас не будет анимации, и навигационный стек будет соблюден.
Измените "анимированное" логическое значение, чтобы воспроизвести анимацию (ы).
Надеюсь, что это поможет!
Ответ 5
Попробуйте дать вашему последнему UIViewController значение StoryBoardID в инспекторе атрибутов в IB. Затем, как только вы определите, есть ли у пользователя пользователь NSUSerDefault:
-Получить ссылку на раскадровку:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
-Использовать экземпляр вашего последнего контроллера представления (не забудьте импортировать последний файл contorllers просмотра .h):
UIViewController *myController = [storyboard instantiateViewControllerWithIdentifier:@"StoryBoardID"];
-Представляем VC:
[self presentViewController:myController animated:YES completion:nil];
Ответ 6
Используйте это:
[self.presentingViewController dismissViewControllerAnimated:NO completion:^{
[self.presentingViewController dismissViewControllerAnimated:NO completion:nil];
}];
Лучшим подходом будет использование UnwindSegue.
Что такое Unwind segues и как вы их используете?
ОБНОВЛЕНИЕ, ЕСЛИ КОММЕНТАРИЙ НЕ ЯВЛЯЕТСЯ
Я не знаю..
возможно, попробуйте это
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *uvc = [storyboard instantiateViewControllerWithIdentifier:@"Details"];
[self.window setRootViewController:uvc];
если нет, тогда сделайте UINavigationController как rootViewController и сделайте ivc в качестве его rootViewController..
Надеюсь, что поможет
Ответ 7
В соответствии с вашим рабочим процессом экрана у вас есть контроллер навигации, и в него вставляются другие контроллеры.
Таким образом, логика проста: если пользователь кэшируется задает детали контроллер представления как корень вашего контроллера просмотра навигации, то ничего не делать (используется раскадровка по умолчанию).
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UINavigationController *nc = (UINavigationController*)self.window.rootViewController;
if (hasUser) {
UIViewController *vc = [nc.storyboard instantiateViewControllerWithIdentifier:@"DetailsViewController"];
nc.viewControllers = @[vc];
}
return YES;
}
ОБНОВЛЕНИЕ
Если вам нужно вернуться к предыдущим контроллерам представлений (например, из деталей в таблицу), вы можете установить всю навигационную иерархию в контроллере навигации:
UINavigationController *nc = (UINavigationController*)self.window.rootViewController;
if (hasUser) {
UIViewController *vc = [nc.storyboard instantiateViewControllerWithIdentifier:@"DetailsViewController"];
[nc pushViewController:vc animated:NO];
}
Ответ 8
Что я делаю в одном из моих приложений, у меня есть дополнительный переход из rootViewController
на экран, который я хочу пропустить (в вашем примере от MyAuth до деталей). Затем, когда я показываю UINavigationController
, я также устанавливаю переменную в rootViewController
, указывающую, хочу ли я перейти к другому экрану. Наконец, в viewDidLoad:
контроллера вида я делаю чек:
// Check if we want to skip to another screen
if (self.skipToDetails == YES) {
[self performSegueWithIdentifier:@"detailsView" sender:nil];
}
Если вы отключите анимацию для этого segue в файле раскадровки, пользователь не увидит первый экран вообще.
Кроме того, вам нужно установить hidesBackButton
на YES
на экране сведений.
Изменить: "unbalanced calls to begin/end"
происходит, когда вы переходите к другому VC (либо push или modal) из оригинального (rootViewController
) VC в viewDidLoad:
. Перемещение кода на viewDidAppear:
должно помочь.
Ответ 9
@Alexander, пожалуйста, проверьте скриншот, вы можете управлять контроллером следующим образом:
![enter image description here]()
И используйте следующий код в контроллере пользовательского вида:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *key = [defaults objectForKey:@"key"];
User *user = [User loadForKey:key];
if (!user) {
MasterViewController *loginController = [self.storyboard instantiateViewControllerWithIdentifier:@"MasterViewController"];
[self presentViewController:masterController animated:YES completion:nil];
}
Ответ 10
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *key = [defaults objectForKey:@"key"];
User *user = [User loadForKey:key];
UINavigationController *navcon = (UINavigationController *)self.window.rootViewController;
if (hasUser) {
UIViewController *viewcon = [navcon.storyboard instantiateViewControllerWithIdentifier:@"DetailsViewController"];
[navcon pushViewController:viewcon animated:NO]; }
}
else
{
UIViewController *viewcon = [navcon.storyboard instantiateViewControllerWithIdentifier:@"UserViewController"];
[navcon pushViewController:viewcon animated:NO];}
}