So I have this BaseCell class which also has this BaseCellViewModel. Of course on top of this lives some FancyViewController with FancyViewModel. The case here is that BaseCell has UIButton on it which triggers this IBAction method - that's fine and that's cool as I can do whatever I want there, but... I have no idea how should I let know FacyViewController about the fact that some action happened on BaseCell.
I can RACObserve a property in FancViewModel as it has NSArray of those cell view models, but how to monitor actual action and notify about exact action triggered on cell?
First thing that came to my mind is the delegation or notifications, but since we have RAC in our project it would be totally stupid not to use it, right?
[Edit] What I did so far...
So, it turns out youc can use RACCommand to actually handle UI events on specific button. In that case I've added:
#property (strong, nonatomic) RACCommand *showAction;
to my BaseCellViewModel with simple implementation like:
- (RACCommand *)showAction {
return [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
NSLog(#"TEST");
return [[RACSignal empty] logAll];
}];
}
And following this pattern I had to do something in my BaseCell which turned out to be quite simple and I ended up with adding:
- (void)configureWithViewModel:(JDLBasePostCellViewModel *)viewModel {
self.viewModel = viewModel;
self.actionButton.rac_command = self.viewModel.showAction;
}
And... It works! But...
I need to present UIActionSheet whenever this happens and this can be show only when I need the current parentViewController and since I don't have this kind of information passed anywhere I don't know what to do right now.
FancyViewModel holds a private #property (nonatomic, strong) NSMutableArray <BaseCellViewModel *> *cellViewModels;, but how can I register something on FancyViewController to actually listen for execution of RACCommand on BaseCellViewModel?
There are a few ways that the cell might communicate with the view controller. A common on is via delegation. Have the cell declare a public delegate, like:
// BaseCell.h
#protocol BaseCellDelegate;
#interface BaseCell : UITableViewCell
#property(nonatomic, weak) id<BaseCellDelegate> delegate;
// ...
#end
#protocol BaseCellDelegate <NSObject>
- (void)baseCell:(BaseCell *)cell didReceiveAction:(NSString *)actionName;
#end
When the button is pressed, work out what you'd like to tell the delegate, and tell it:
// BaseCell.m
- (IBAction)buttonWasPressed:(id)sender {
self.delegate baseCell:self didReceiveAction:#"someAction";
}
Then, in the view controller, declare that you conform to the protocol:
// FancyViewController.m
#interface FancyViewController () <BaseCellDelegate>
in cellForRowAtIndexPath, set the cell's delegate:
// dequeue, etc
cell.delegate = self;
You'll now be required to implement this in the vc:
- (void)baseCell:(BaseCell *)cell didReceiveAction:(NSString *)actionName {
// the cell got an action, but at what index path?
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// now we can look up our model at self.model[indexPath.row]
}
UITableViewCell are volatile, reusable components. They come and go, and your button will do as well.
How about following #danh suggestion and once control is in View Controller, formulate a RAC signal programmatically.
Since I feel rather belonging to RxSwift camp :) I cannot provide source snippet, but this answer is probably what I meant.
As you already have a RACCommand in BaseCellViewModel, you can use one of its convenience signals. For example, you can track its state using executing signal:
[baseCellViewModel.showAction.executing subscribeNext:^(NSNumber *executing) {
//do something if the command is executing
}];
Bindings with RACObserve will work as well, if you need them.
You can also get the latest value from the command's underlying signal (but in the code you posted it won't work, as you use [RACSignal empty] with doesn't send any 'next' values):
[[baseCellViewModel.command.executionSignals switchToLatest] subscribeNext:^(id x) {
//do something with the value
}];
Note that you should subscribe to this signals when you create BaseCellViewModel, not in configureWithViewModel as the latter will be called many times (resulting in many subscriptions for the same signal).
Related
I have a UITableView comprised of custom UITableViewCells. In each cell, there is a UILabel and a UISlider. Does anyone know how to, upon a change in value of one of the sliders, send the new value of the slider from the custom UITableViewCell (in a separate file) to the UITableViewController, so that I can then update the array from which the table was populated?
The closest I've got so far is a failed hack: firing a setSelected event when a slider value is changed. Whilst this highlights the changed custom cell, the event is not picked up by didSelectRowAtIndexPath in the UITableViewController.
Whilst code is always appreciated, a conceptual/method solution is what I am looking for.
Thank you in advance,
Jamie
What you need is called Delegate Pattern.
Quoting from there to explain what does it mean:
Delegation is a simple and powerful pattern in which one object in a
program acts on behalf of, or in coordination with, another object.
The delegating object keeps a reference to the other object—the
delegate—and at the appropriate time sends a message to it. The
message informs the delegate of an event that the delegating object is
about to handle or has just handled. The delegate may respond to the
message by updating the appearance or state of itself or other objects
in the application, and in some cases it can return a value that
affects how an impending event is handled. The main value of
delegation is that it allows you to easily customize the behavior of
several objects in one central object.
These diagrams will help you understand what goes on:
Architecture:
Operation:
Now as to how to implement it, this is what you have to do.
For Objective-C:
First of all, create delegate methods of your UITableViewCell. Lets name it ContactTableViewCell.
In your ContactTableViewCell.h file, do this:
#protocol ContactCellDelegate <NSObject>
#required
-(void) didMoveSliderWithValue:(float) value;
#end
#interface ContactTableViewCell : UITableViewCell
#property (weak, nonatomic) id<ContactCellDelegate> delegate;
Now conform your TableViewController to this delegate. Let's name your VC MyTableViewController.
In MyTableViewController.h, Do this:
#interface MyTableViewController : UIViewController <ContactCellDelegate> //Use UITableViewController if you are using that instead of UIViewController.
In your cellForRowAtIndexPath, before returning cell, add this line:
cell.delegate = self;
Add implementation for the delegate method INSIDE your MyTableViewController.m.
-(void) didMoveSliderWithValue: (float) value
{
NSLog(#"Value is : %f",value);
//Do whatever you need to do with the value after receiving it in your VC
}
Now let's get back to your ContactTableViewCell.m. In that file you must have added some IBAction to capture the value change event in slider. Let's say it is the following:
- (IBAction)sliderValueChanged:(UISlider *)sender {
self.myTextLabel.text = [#((int)sender.value) stringValue]; //Do whatever you need to do in cell.
//Now call delegate method which will send value to your view controller:
[delegate didMoveSliderWithValue:sender.value];
}
When you call delegate method, it will run the implementation that we wrote earlier in the MyTableViewController. Do whatever you need in that method.
What happens here is that your Cell sends the message to your desired VC (Which is delegate of the Cell), that "Hey, Call the delegate method that we wrote earlier in your body. I am sending you parameters right away". Your VC takes the parameters and does whatever you wanted it to do with that info and at that time.
For Swift:
First of all, your TableViewCell.swift file, create a protocol like this:
#class_protocol protocol ContactCellDelegate {
func didMoveSliderWithValue(value: Float)
}
Now in your Cell class, create a delegate property like:
var cellDelegate: ContactCellDelegate?
In your Slider IBAction, call the delegate method like this:
self.cellDelegate?.didMoveSliderWithValue(slider.value)
In your VC do these changes:
Make it conform to the delegate:
class MyTableViewController: UIViewController, ContactCellDelegate
Add this line before returning cell in cellForRowAtIndexPath
cell.cellDelegate = self //Dont forget to make it conform to the delegate method
Add the implementation of required delegate method:
func didMoveSliderWithValue(value:float) {
//do what you want
}
I have kept the Swift part precise and summarized because It should be very easy to change the detailed Obj-C explanation to Swift implementation. However If you are confused about any of the pointers above, leave a comment.
Also see: StackOverflow answer on using Delegate pattern to pass data back
Screencast: https://www.youtube.com/watch?v=ZwehDwITEyI
This is really bizarre. The problem is to do with a label outlet sitting in a custom-designed table cell. That cell is of my CustomCell class. (actually called RA_FormCell if you watch the screencast).
CustomCell.h
#property (weak, nonatomic) IBOutlet UILabel *dayOutlet;
-(void)controller:(id<CustomCellDelegate>)controller didUpdateDay:(NSString *)theDay;
CustomCell.m
// Method is called by a view controller
// (which is itself a delegate of the CustomCell class,
// hence the identifier you see below)
-(void)controller:(id<CustomCellDelegate>)controller didUpdateDay:(NSString *)theDay;
{
NSLog(#"Method called") // confirms to me that method is called
self.dayOutlet.text = #"Goodmorning";
NSLog(#"%#", self.dayOutlet.text); // displays (null)
}
That final log does actually appear, so the method is definitely being called. I have discounted the following:
self.dayOutlet.text is not written to elsewhere by any other method in the project
dayOutlet is connected to the label in the storyboard (and the label is not connected to anything else)
The label is not hidden underneath some accidental static label on the storyboard
The cell attributes on the storyboard include its class as CustomCell
No warnings or alerts in Xcode (I have been careful to avoid any circular imports)
The problem was that the controller:didUpdateDay: message was not sent to the correct instance of the cell class.
This occurred because I had not correctly assigned this cell to be the delegate for the view controller. For anyone interested, in my screencast at 3:50, you can see that I have the following in cellForRowAtIndexPath:
RA_FormCell *cell = [tableView dequeueReusableCellWithIdentifier:self.formCellArray[indexPath.row] forIndexPath:indexPath];
self.delegate = cell
However, this means that self.delegate got continually overwritten as the table cells were generated. As a result, my controller:didUpdateDay message was sent to the bottom cell of the table, and not the top one as I required.
The solution was simple - there's no need to have this second delegate at all. Instead, when the cell delegates to the view controller, it should pass self into the message it delegates:
id<CustomCellDelegate> strongDelegate = self.delegate;
if ([strongDelegate respondsToSelector:#selector(customCell:didChangeDay1:)])
[strongDelegate customCell:self didChangeDay1:[sender value]];
Then, in the implementation of this method by the delegate, simply end it by changing the outlet directly:
-(void)customCell:(RA_FormCell *)customCell didChangeDay1:(double)value
// put logic here
customCell.dayOutlet.text = #"No problem!";
In general, there should rarely be a need for a two-way delegate structure. Keep it one way, from A to B, and just remember to have A pass self in any messages it sends to B. That way, B will know the object the message came from, and be able to communicate back to A.
Thanks to Paulw11
I'm really going crazy on this.
But let me explain to you my little project first:
I have an Custom UITableView TDStartTableView.
Also in there I have some methods implemented for rendering the table. No problem there.
Inside of one TableViewCell there is a button.
When that Button is clicked it triggers this method:
- (void)triggerPush {
[self.delegate pushNextView];
}
self.delegate is specified in the .h file of TDStartTableView like this:
#property (nonatomic, weak) id<TDStartTableViewDelegate> delegate;
Also, the reference is set in my UITableViewController:
self.tableView.delegate = self;
So essentially what I'm trying to do is: Create a custom UITableView with Buttons etc. and then listen on the events from a ViewController that is implementing that UITableView and the protocol
So because the protocol forces me to implement pushNextView this method is in my UIViewController:
- (void)pushNextView {
NSLog(#"This works");
}
To this point everything works just fine, no problem there!
But now comes the tricky part.
I create a segue from my UIViewController to a new ViewController. I connect them via a segue and name the segue appropriately. pushToSecondStep.
Now one would think, that when I change the implementation of pushNextView to this
- (void)pushNextView {
[self performSegueWithIdentifier:#"pushToSecondStep" sender:self];
}
it works. But what I get is:
'Receiver (<TDFirstStepTableViewController: 0x8dc97d0>) has no segue with identifier 'pushToSecondStep''
Please help, I'm going crazy :D
The problem was, that I overwrote a constructor of TDStartTableView.
The proper form is that you implement all constructors, so that Objective-C can instantiate them all by itself:
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
return self;
}
Also when you implement a custom UITableView Widget you shouldn't use UITableViewController but UIViewController.
Also you don't initialize your custom UITableView yourself, Storyboard already does that for you, so if you want to set special variables for it like numberOfRows then just declare a property and then set it via a setter-method outside and call [tableView reloadData].
Also, thanks https://stackoverflow.com/users/1095089/shubhank for helping me in the chat :)
What is the best way to pass data from a child modal view to the parent view controller?
I have a Child Modal Login Screen on my iPad app that I want to pass back user information to the parent Split View Controller.
I am thinking of using NSNotification, but I am not sure if this is the easiest/most efficient way to pass data back to the parent.
Thanks!
Alan
I would suggest, as iPatel did, to use delegation to solve your problem. The relationship between the parent view controller and the login view controller makes this pattern appropriate. When one object creates another in order to fulfill a specific responsibility, one should consider delegation as a way to have the created object communicate with the creator. A particularly compelling reason to choose delegation would be if the task to be accomplished potentially has multiple steps that require a high level of interaction between objects. You can look at the NSURLConnectionDelegate protocol as an illustration of this. Connecting to a URL is a complex task, involving stages such as processing responses, meeting authentication challenges, saving downloaded data, and handling errors, the connection and the delegate handle this together over the lifetime of the connection.
As you've probably noticed, in Objective-C protocols are used to achieve delegation without tightly coupling the created object (in this case your login view controller) to the object that created it (the parent view controller). The login view controller can then interact with any object that can receive the messages defined in its protocol, rather than rely on any particular class implementation. Tomorrow, if you receive a requirement to allow any view controller to show the login view, the login view controller wouldn't need to change. Your other view controllers can implement its delegate protocol, create and present the login view, and assign themselves as delegates without the login view controller ever knowing of their existence.
Some delegation examples you'll find on Stack Overflow may be very confusing and very un-like what's found in the built-in frameworks. One must pick the names and interfaces of the protocols carefully, as well as the responsibilities assigned to each object, so that code reuse is maximized and the objective of the code is achieved.
You should first take a look at the many delegate protocols within the built-in frameworks for an idea of what the relationship looks like when expressed in code. Here is another small example, based on your login use case. I hope you will find that the purpose of the delegation is clear and that the roles and responsibilities of the objects involved are clear and expressed through their names within the code.
First, let's look at the LoginViewController's delegate protocol:
#import <UIKit/UIKit.h>
#protocol LoginViewControllerDelegate;
#interface LoginViewController : UIViewController
// We choose a name here that expresses what object is doing the delegating
#property (nonatomic, weak) id<LoginViewControllerDelegate> delegate;
#end
#protocol LoginViewControllerDelegate <NSObject>
// The methods declared here are all optional
#optional
// We name the methods here in a way that explains what the purpose of each message is
// Each takes a LoginViewController as the first argument, allowing one object to serve
// as the delegate of many LoginViewControllers
- (void)loginViewControllerDidLoginSuccessfully:(LoginViewController *)lvc;
- (void)loginViewController:(LoginViewController *)lvc didFailWithError:(NSError *)error;
- (void)loginViewControllerDidReceivePasswordResetRequest:(LoginViewController *)lvc;
- (void)loginViewControllerDiDReceiveSignupRequest:(LoginViewController *)lvc;
- (BOOL)loginViewControllerShouldAllowAnonymousLogin:(LoginViewController *)lvc;
#end
The login controller can communicate a number of events to its delegate, as well as ask its delegate for information used to customize its behavior. It communicates events to the delegate in its implementation as part of its response to user actions:
#import "LoginViewController.h"
#interface LoginViewController ()
#property (weak, nonatomic) IBOutlet UIButton *anonSigninButton;
#end
#implementation LoginViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Here we ask the delegate for information used to layout the view
BOOL anonymousLoginAllowed = NO;
// All our protocol methods are #optional, so we must check they are actually implemented before calling.
if ([self.delegate respondsToSelector:#selector(loginViewControllerShouldAllowAnonymousLogin:)]) {
// self is passed as the LoginViewController argument to the delegate methods
// in this way our delegate can serve as the delegate of multiple login view controllers, if needed
anonymousLoginAllowed = [self.delegate loginViewControllerShouldAllowAnonymousLogin:self];
}
self.anonSigninButton.hidden = !anonymousLoginAllowed;
}
- (IBAction)loginButtonAction:(UIButton *)sender
{
// We're preteneding our password is always bad. So we assume login succeeds when allowed anonmously
BOOL loginSuccess = [self isAnonymousLoginEnabled];
NSError *loginError = [self isAnonymousLoginEnabled] ? nil : [NSError errorWithDomain:#"domain" code:0 userInfo:nil];
// Fake concurrency
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// Notify delegate of failure or success
if (loginSuccess) {
if ([self.delegate respondsToSelector:#selector(loginViewControllerDidLoginSuccessfully:)]) {
[self.delegate loginViewControllerDidLoginSuccessfully:self];
}
}
else {
if ([self.delegate respondsToSelector:#selector(loginViewController:didFailWithError:)]) {
[self.delegate loginViewController:self didFailWithError:loginError];
}
}
});
}
- (IBAction)forgotPasswordButtonAction:(id)sender
{
// Notify delegate to handle forgotten password request.
if ([self.delegate respondsToSelector:#selector(loginViewControllerDidReceivePasswordResetRequest:)]) {
[self.delegate loginViewControllerDidReceivePasswordResetRequest:self];
}
}
- (IBAction)signupButtonAction:(id)sender
{
// Notify delegate to handle signup request.
if ([self.delegate respondsToSelector:#selector(loginViewControllerDiDReceiveSignupRequest:)]) {
[self.delegate loginViewControllerDiDReceiveSignupRequest:self];
}
}
- (BOOL)isAnonymousLoginEnabled
{
BOOL anonymousLoginAllowed = NO;
if ([self.delegate respondsToSelector:#selector(loginViewControllerShouldAllowAnonymousLogin:)]) {
anonymousLoginAllowed = [self.delegate loginViewControllerShouldAllowAnonymousLogin:self];
}
return anonymousLoginAllowed;
}
#end
The main view controller instantiates and presents a login view controller, and handles its delegate messages:
#import "MainViewController.h"
#import "LoginViewController.h"
#define LOGGED_IN NO
#interface MainViewController () <LoginViewControllerDelegate>
#end
#implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Fake loading time to show the modal cleanly
if (!LOGGED_IN) {
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// Create a login view controller, assign its delegate, and present it
LoginViewController *lvc = [[LoginViewController alloc] init];
lvc.delegate = self;
[self presentViewController:lvc animated:YES completion:^{
NSLog(#"modal completion finished.");
}];
});
}
}
#pragma mark - LoginViewControllerDelegate
- (void)loginViewControllerDidLoginSuccessfully:(LoginViewController *)lvc
{
NSLog(#"Login VC delegate - Login success!");
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)loginViewController:(LoginViewController *)lvc didFailWithError:(NSError *)error
{
// Maybe show an alert...
// UIAlertView *alert = ...
}
- (void)loginViewControllerDidReceivePasswordResetRequest:(LoginViewController *)lvc
{
// Take the user to safari to reset password maybe
NSLog(#"Login VC delegate - password reset!");
}
- (void)loginViewControllerDiDReceiveSignupRequest:(LoginViewController *)lvc
{
// Take the user to safari to open signup form maybe
NSLog(#"Login VC delegate - signup requested!");
}
- (BOOL)loginViewControllerShouldAllowAnonymousLogin:(LoginViewController *)lvc
{
return YES;
}
#end
Logging in can be a complex, interactive process in some ways, so I do recommend you seriously consider using delegation instead of notifications. However, one thing that may be problematic is that delegates are necessarily only a single object. If you need to have multiple, disparate objects know about the login view controller's progress and stae, then you may need to use notifications. Especially if the login process can be constrained to be very simple, in a way that does not require any interaction beyond passing one-way messages and data, then notifications can become a viable option. You can pass arbitrary variables in a notification back inside the userInfo property which is an NSDictionary of whatever you decide to stuff in it. Notifications can impact performance but I understand that only happens nowadays when observers number in the hundreds. Even still, it is not the most natural fit in my mind, as you have the parent object (that more or less controls the lifetime of the child) asking a third party object for updates from the child object.
You can get it by using Protocol, it is Best way.
I will give you the Basic Idea for how to create a Protocol
Also, read this question:
How do I create delegates in Objective-C?
Following code give you the basic idea for Protocol, here in below code you can get Button title from MasterViewController to DetailViewController.
#DetailViewController.h
#import <UIKit/UIKit.h>
#protocol MasterDelegate <NSObject>
-(void) getButtonTitile:(NSString *)btnTitle;
#end
#interface DetailViewController : MasterViewController
#property (nonatomic, assign) id<MasterDelegate> customDelegate;
#DetailViewController.m
if([self.customDelegate respondsToSelector:#selector(getButtonTitile:)])
{
[self.customDelegate getButtonTitile:button.currentTitle];
}
#MasterViewController.m
create obj of DetailViewController
DetailViewController *obj = [[DetailViewController alloc] init];
obj.customDelegate = self;
[self.navigationController pushViewController:reportTypeVC animated:YES];
and add delegate method in MasterViewController.m for get button title.
#pragma mark -
#pragma mark - Custom Delegate Method
-(void) getButtonTitile:(NSString *)btnTitle;
{
NSLog(#"%#", btnTitle);
}
My goal is to notify a UITableView to refresh itself every time some configurations have changed. The problem is that the configuration view is "not" on the same view that produces the signal. (Yes, I used Tabbed Application.)
Currently I use a sort of global variable in AppDelegate for detecting the change in one view, and do the check in another view. This is fine but the code is not readable as it is so tightly coupling. Is there an elegant method for doing this? Do I miss something in this programming framework?
If there were such an elegant way, I suppose the refreshing process of UITableView should happen as soon as the notification occurs. In this case, I would like to know whether it's possible to delay UITableView from refreshing itself until viewDidAppear occurs.
I would use KVO (Key Value Observing) to keep track of when it changes:
- (void)viewDidLoad {
[super viewDidLoad];
// Note that you can use the options to get the new value passed when it
// changes if you want to update immediately.
[configurationObject addObserver:self forKeyPath:#"configurationItem" options:0 context:nil];
}
- (void)viewDidUnload {
[super viewDidUnload];
[configurationObject removeObserver:self forKeyPath:#"configurationItem"];
}
// Note that I would refresh in viewWillAppear instead of viewDidAppear
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.needToRefreshData == YES) {
[self.tableView refreshData];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (keyPath isEqualToString:#"configurationItem") {
[self.needToRefreshData = YES];
}
}
Use Delegation Design Pattern to pass data from one View Controller to the Other.
For example, let's say one Tab shows a list of cars in a UITableViewController and you have another view that let's a user add a new car to the list. You can let the UITableViewController
Adopt AddCarViewController's protocol
Set itself as a Delegate for AddCarViewController's protocol
Implement its protocol method
Execute the protocol method when informed
You can then let the AddCarViewController
Create a Protocol
Declare object reference Delegate with getter and setter methods
Define a method under that protocol
Inform the Delegate when the Save action is performed
Take a look at the following sample code for your UITableViewController
#interface ViewController : UITableViewController <AddCarViewControllerDelegate>
:
:
// The addCar: method is invoked when the user taps the Add button created at run time.
- (void)addCar:(id)sender
{
// Perform the segue named ShowAddCar
[self performSegueWithIdentifier:#"ShowAddCar" sender:self];
}
:
:
// This method is called by the system whenever you invoke the method performSegueWithIdentifier:sender:
// You never call this method. It is invoked by the system.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSString *segueIdentifier = [segue identifier];
if ([segueIdentifier isEqualToString:#"ShowAddCar"]) {
// Obtain the object reference of the destination view controller
AddCarViewController *addCarViewController = [segue destinationViewController];
// Under the Delegation Design Pattern, set the addCarViewController's delegate to be self
addCarViewController.delegate = self;
// Instantiate a Save button to invoke the save: method when tapped
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:addCarViewController action:#selector(save:)];
// Set up the Save custom button on the right of the navigation bar
addCarViewController.navigationItem.rightBarButtonItem = saveButton;
}
}
:
:
- (void)addCarViewController:(AddCarViewController *)controller didFinishWithSave: (BOOL)save {
:
:
}
Sample code for the AddCarViewController is here
#protocol AddCarViewControllerDelegate;
#interface AddCarViewController : UIViewController
#property (nonatomic, strong) IBOutlet UITextField *carMake;
#property (nonatomic, strong) IBOutlet UITextField *CarName;
#property (nonatomic, assign) id <AddCarViewControllerDelegate> delegate;
// The keyboardDone: method is invoked when the user taps Done on the keyboard
- (IBAction)keyboardDone:(id)sender;
// The save: method is invoked when the user taps the Save button created at run time.
- (void)save:(id)sender;
#end
/*
The Protocol must be specified after the Interface specification is ended.
Guidelines:
- Create a protocol name as ClassNameDelegate as we did above.
- Create a protocol method name starting with the name of the class defining the protocol.
- Make the first method parameter to be the object reference of the caller as we did below.
*/
#protocol AddCarViewControllerDelegate
- (void)addCarViewController:(AddCarViewController *)controller didFinishWithSave:(BOOL)save;
#end
Well, one approach would be to have some common class (singleton perhaps which app delegate kind of is) that keeps track of your model, when the settings viewController detects a change it can mark the model as changed, then when the view in question comes in to view, ie, viewDidAppear gets called, it can query the model to see if the changed flag has been set, if it has then you know to reload the table view, otherwise you dont...
Another way could be to use notification center for it, if your view is loaded it can sign up for the notifications of the model change, in which at point it sets a flag that it needs to reload the table view next time it comes on screen..
hope this helps
You could store the configuration in core data and use an NSFetchedResultsController with the dependant view controller set as a delegate. This way your view controller will get a callback whenever the data is changed.
Apple has some boilerplate code to handle the updates as well