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?
问题:
回答1:
Reusing instances of your view controllers is actually very straightforward, and it doesn't require modifying SWRevealViewController.
In your designated MenuViewController (the view controller responsible for invoking segues whenever you wish to show a menu item's view controller), create a view controller cache. This will be used to store view controller instances whenever they are created via segue:
@property (nonatomic, strong) NSMutableDictionary *viewControllerCache;
We'll initialise this later when needed.
Whenever you respond to selecting menu items, instead of invoking a segue directly, call a method instead that checks the cache:
- (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;
}
}
This cache checking method could look something like this:
- (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];
}
}
In this scenario, I create a key in the cache as a combination of the segue name and the sender parameter. I'm assuming the sender is a string here for the sake of an example. It's quite possible that the sender parameter is not a string, so you should probably do some checking to ensure it doesn't crash.
I then check the view controller cache if any view controller exists. If so, perform the view controller swapping you normally do in prepareForSegue:sender:
(you would have pasted this snippet in there when you first set up your SWRevealViewController). If it doesn't exist, perform the segue as normal (which will create a new instance).
All that's left now is to modify your prepareForSegue:sender:
method to store a reference to the instantiated view controller in the cache:
- (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];
};
}
}
Notice how I'm just appending 2-3 lines to prepareForSegue:sender:
, which shouldn't interfere with your existing setup. It's possible that the segue identifier isn't set, which will result in a crash. You should use identifiers on your segues so you can identify them for caching.
One limitation with this approach is that this only caches view controllers that were invoked with segues from the menu view controller. You'll notice that selecting the menu item for the first visible view controller will cause it to be reloaded from scratch (because it wasn't cached yet). Any time after that, it should work with the cache. I imagine shifting the caching to the SWRevealController that sits before your menu setup in the storyboard will alleviate this.
回答2:
You can use UITabBarController for store and reusing ViewControllers Just make your UITabBarController frontViewController via sw_front segue, then switch viewControllers from rear viewController
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)
}