Reload and not reload if press back from different view controllers. Swift
Here is a link to a question I answered a couple days ago. Use the navigation controller delegate to handle the back button. In your second view controller, set the delegate to self and reload the tableview when you press the back button.
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
if let controller = viewController as? FirstViewController {
controller.tableView.reloadData()
}
}
NOTE: I'm assuming you're using the back button of the navigation controller here.
EDIT: Another example using your manually added back button:
@IBAction func backButtonTapped(sender: AnyObject) {
if let viewControllers = app.window?.rootViewController?.childViewControllers {
viewControllers.forEach { ($0 as? FirstViewController)?.tableView.reloadData() }
}
self.dismissViewControllerAnimated(true, completion: nil)
}
Seeing as you are using a navigation controller:
@IBAction func backButtonTapped(sender: AnyObject) {
navigationController?.viewControllers.forEach { ($0 as? FirstViewController)?.tableView.reloadData() }
self.dismissViewControllerAnimated(true, completion: nil)
}
If displaying vc2 is performed by vc1 and is always sure to invalidate the data in vc1, you could do the following:
- add a
needsReload
boolean instance variable to vc1 - set it to true whenever you display vc2 (and when instanciating vc1 eg in
awakeFromNib
if coming from a storyboard) - only perform the content of
loadTable
if needsReload is true (maybe refactor this logic into aloadTableIfNeeded
) - don't forget to set
needsReload
to false in the end ofloadTableIfNeeded
This invalidation pattern is found throughout UIKit, see for example UIView setNeedsLayout
/layoutIfNeeded
. The advantage is that even if several events cause the data to invalidate, it will only actually get refreshed when you need it.
In your situation it has the additional advantage of keeping the logic contained in vc1 and not creating unnecessary coupling between your VCs, which is always good.
---UPDATE: sample implementation (ObjC but you'll get the idea)
You only need to handle this in VC1, forget about all the back button stuff in VC2. This implementation will mark VC1 for reload as soon as VC2 is presented, but will actually reload only on viewWillAppear, when VC2 is dismissed.
---UPDATE 2: Added a conditional reload based on a delegate callback
Note that _needsReload
is now set in the delegate callback, not when VC2 is first presented. Instead we set VC1 as the delegate of VC2. (_needsReload
logic is actually unnecessary using this method, kept it for reference)
//VC2: add a delegate to the interface
@class VC2;
@protocol VC2Delegate
- (void) viewController:(VC2*)myVC2 didFinishEditingWithChanges:(BOOL)hasChanges;
@end
@interface VC2
@property (nonatomic, weak) id<VC2Delegate> delegate
@end
@implementation VC2
- (IBAction) finishWithChanges
{
[self.delegate viewController:self didFinishEditingWithChanges:YES];
}
- (IBAction) finishWithoutChanges
{
[self.delegate viewController:self didFinishEditingWithChanges:NO];
}
@end
//VC1: implement the VC2Delegate protocol
@interface VC1 () <VC2Delegate>
@end
@implementation VC1
{
BOOL _needsReload
}
- (void) awakeFromNib
{
//adding this for completeness but the way you did it in Swift (at init) is correct
[super awakeFromNib];
_needsReload = YES;
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self reloadTableIfNeeded];
}
- (IBAction) displayVC2
{
VC2* myVC2 = ... //instanciate your VC2 here
myVC2.delegate = self; //set as the delegate of VC2
[self presentViewController:myVC2 animated:YES completion:nil];
}
- (void) viewController:(VC2*)myVC2 didFinishEditingWithChanges:(BOOL)hasChanges
{
_needsReload = hasChanges;
[self reloadTableIfNeeded];
}
- (void) reloadTableIfNeeded
{
if (_needsReload) {
[self.tableView reloadData];
_needsReload = NO;
}
}
@end