I am working with SWRevealViewController, and I am using the custom segue, I notice that everytime segue is performed SWRevealViewController creates a brand new instance of the destination controller, is there a way to have SWRevealViewController reuse viewcontrollers?
2 回答
重用视图控制器的实例实际上非常简单,并且不需要修改 SWRevealViewController。
在您指定的 MenuViewController(当您希望显示菜单项的视图控制器时负责调用 segues 的视图控制器)中,创建视图控制器缓存。这将用于在通过 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];
};
}
}
请注意我只是将 2-3 行附加到prepareForSegue:sender:
,这不应该干扰您现有的设置。可能没有设置 segue 标识符,这将导致崩溃。你应该在你的 segues 上使用标识符,这样你就可以识别它们以进行缓存。
这种方法的一个限制是它只缓存从菜单视图控制器中使用 segues 调用的视图控制器。您会注意到为第一个可见视图控制器选择菜单项将导致它从头开始重新加载(因为它尚未缓存)。之后的任何时候,它都应该与缓存一起使用。我想将缓存转移到位于情节提要中的菜单设置之前的 SWRevealController 将缓解这种情况。
您可以使用 UITabBarController 存储和重用 ViewControllers 只需通过 sw_front segue 使您的 UITabBarController frontViewController,然后从后视图控制器切换 viewControllers
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//get tabBarController
let tabBarVC = revealViewController().frontViewController as? UITabBarController
//show selected ViewController
tabBarVC.selectedIndex = indexPath.item
revealViewController().revealToggle(animated: true)
}