我尝试了几种开源滑动菜单导航,模仿Facebook,Gmail应用(ViewDeck,MFSideMenu和SWRevealViewController中的导航菜单,然后进入同样的问题,每当我根据菜单选择进入另一个ViewController,然后回到原始VC时,VC总是重新加载,因此以前以编程方式添加的所有子视图都会消失。即使我只是从菜单中选择相同的VC,它仍然会被加载。我试图在App Delegate中保留一个强大的指向视图控制器的指针(通过实例化该视图的属性),但这还没有解决问题?还有另一种解决方法吗?谢谢!
我目前正在使用SWRevealViewController,并将我的Storyboard设置为这样。
更新以及对我有用的内容:
最终,一切都与MFSideMenu和 danh 的建议一致,以保持我感兴趣的控制器保留在导航堆栈中。
以下是我选择非主要视图控制器的代码
- (IBAction)buttonForNonPrimaryVC:(UIButton *)sender
{
someVC *someVC = [self.storyboard instantiateViewControllerWithIdentifier:@"someIdentifier"];
UINavigationController *navigationController = self.menuContainerViewController.centerViewController;
[navigationController pushViewController:someVC animated:NO];
[self.menuContainerViewController setMenuState:MFSideMenuStateClosed];
}
选择主视图控制器:
- (IBAction)primaryVCTapped:(UIButton *)sender {
if ([[[self.menuContainerViewController.centerViewController viewControllers] lastObject] isKindOfClass: [primaryVC class]] || [[self.menuContainerViewController.centerViewController viewControllers] count] < 2){
//Do nothing
} else {
[self.menuContainerViewController.centerViewController popViewControllerAnimated:NO];
}
[self.menuContainerViewController setMenuState:MFSideMenuStateClosed];
}
答案 0 :(得分:2)
特别是对于SWRevealViewController,重用视图控制器的实例实际上非常简单,并且不需要修改SWRevealViewController。
在指定的MenuViewController中(负责在您希望显示菜单项的视图控制器时调用segue的视图控制器),创建一个视图控制器缓存。这将用于存储视图控制器实例,只要它们是通过segue创建的:
@property (nonatomic, strong) NSMutableDictionary *viewControllerCache;
我们稍后会在需要时将其初始化。
每当您选择菜单项时,不是直接调用segue,而是调用一个检查缓存的方法:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// your logic will vary here, this is just an example
switch(indexPath.row)
{
case 0:
[self showViewControllerForSegueWithIdentifier:@"showSomething" sender:nil];
break;
default:
break;
}
}
此缓存检查方法可能如下所示:
- (void)showViewControllerForSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
NSString *cacheKey = [identifier stringByAppendingFormat:@":%@", sender];
UIViewController *dvc = [self.viewControllerCache objectForKey:cacheKey];
if(dvc)
{
NSLog(@"reusing view controller from cache");
UINavigationController* navController = (UINavigationController*)self.revealViewController.frontViewController;
[navController setViewControllers: @[dvc] animated: NO ];
[self.revealViewController setFrontViewPosition: FrontViewPositionLeft animated: YES];
}
else
{
NSLog(@"creating view controller from segue");
[self performSegueWithIdentifier:identifier sender:sender];
}
}
在这种情况下,我在缓存中创建一个密钥,作为segue名称和sender参数的组合。为了举个例子,我假设发送者是一个字符串。 sender参数很可能不是字符串,所以你应该做一些检查以确保它不会崩溃。
然后检查视图控制器缓存是否存在任何视图控制器。如果是这样,请执行您在prepareForSegue:sender:
中通常执行的视图控制器交换(当您第一次设置SWRevealViewController时,您将在此处粘贴此代码段)。如果它不存在,则正常执行segue(这将创建一个新实例)。
现在剩下的就是修改你的prepareForSegue:sender:
方法来存储对缓存中实例化视图控制器的引用:
- (void) prepareForSegue:(UIStoryboardSegue *) segue sender:(id) sender
{
if([segue.identifier isEqualToString:@"showSomething"])
{
// do whatever you wish to the destination view controller here
// ...
}
// this part should be very familiar
if ( [segue isKindOfClass: [SWRevealViewControllerSegue class]] )
{
SWRevealViewControllerSegue *swSegue = (SWRevealViewControllerSegue*) segue;
swSegue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) {
// cache the view controller
if(self.viewControllerCache == nil) self.viewControllerCache = [NSMutableDictionary dictionary];
NSString *cacheKey = [segue.identifier stringByAppendingFormat:@":%@", sender];
[self.viewControllerCache setObject:dvc forKey:cacheKey];
// the rest remains as before
UINavigationController* navController = (UINavigationController*)self.revealViewController.frontViewController;
[navController setViewControllers: @[dvc] animated: NO ];
[self.revealViewController setFrontViewPosition: FrontViewPositionLeft animated: YES];
};
}
}
请注意我只是在prepareForSegue:sender:
附加2-3行,这不应该干扰您现有的设置。可能没有设置segue标识符,这将导致崩溃。您应该在segue上使用标识符,以便识别它们以进行缓存。
这种方法的一个限制是,这只会缓存使用菜单视图控制器中的segue调用的视图控制器。您会注意到,为第一个可见视图控制器选择菜单项将导致从头开始重新加载(因为它尚未缓存)。在此之后的任何时候,它都应该与缓存一起使用。我想将缓存转移到位于故事板中菜单设置之前的SWRevealController将缓解这种情况。你可以通过继承它来做到这一点。
答案 1 :(得分:1)
我有一个想要使用MFSideMenu的客户端,我使用的解决方案是将UINavigationVC作为中心vc。该应用程序使用弹出和推送来导航,而不是segues。当用户与推送视图控制器交互时,这具有在应用程序的主视图控制器中维护所需的行为。
侧面菜单是表格视图,因此当选择一个表格行时,会向中心vc的根vc发送一条消息,它基本上就是这样:
- (void)sideMenu:(MFSideMenuContainerViewController *)menuVC didSelectItemAtIndex:(NSInteger)index {
// we keep an array of vs classes to instantiate for each menu item
Class *klass = self.menuVCClasses[index];
// client also wanted each vc in it's own nib. I would have preferred storyboard, but...
NSString *nibName = NSStringFromClass(klass);
UIViewController *vc = [[klass alloc] initWithNibName:nibName bundle:[NSBundle mainBundle]];
// or from storyboard
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass(klass)];
// here's the punch line. pop without animation, push with animation
// this will keep the main vc around at the bottom of the stack
[self.navigationController popToRootViewControllerAnimated:NO];
[self.navigationController pushViewController:vc animated:YES];
}
答案 2 :(得分:0)
当您进行segue时,始终会创建一个视图控制器的新实例。使用.xib文件而不是故事板可以让您重用视图控制器并保持其他视图的活动。
答案 3 :(得分:0)
备用解决方案是在您的侧面菜单控制器中添加一个委托,然后执行
在用户导航的视图控制器中委托点击。