Refreshing background view from modal view - ios

I want to update the view controller that is covered with a modal view in my application, I'm using swift. What I have currently is this:
The view on the right is the caller, and the views on the left are the modal views triggered by each button from the first one. They do basic edit, add new operations. These views are modally presented over the main one, and what I want to do is update the table view controllers enbedded in the two containers once I save the data from one of the modals.
I researched the use of one of the viewLoad events, but I'm kinda stuck at the moment. How can I do this?

Well you can do it in two ways
1. Delegate
2. NSNotificationCenter
In your Model class.h file
#protocol someProtoColName<NSObject>
-(void)finishedDoingMyStuff;
#end
#interface ModelClass()
#property (nonatomic,weak) id<someProtoColName> delegateObj;
Then in your ModelClass.m
-(void)someactionHappend{
//this is the method where you save your things call the delegate method inside here like this.
[self.delegateObj finishedDoingMyStuff];
}
Then in your CallerClass.h
#import ModelClass.h
#interface CallerClass:UIViewController<someProtoColName>
Inside CallerClass.m viewDidLoad
-(void)viewDidLoad{
ModelClass *model = [[ModelClass alloc]init];
model.delegateObj = self;
}
//Now declare the delegate method
-(void)finishedDoingMyStuff{
//update your code that this has happend. this will be called when your model class's button action inside which you sent the `self.delegateObj`message is invoked instantaneously.
}
NSNotificationCenter
CallerClass.m
-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(someMethod) name:#"NOTIFICATIONNAME" object:nil];
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter]removeObserver:self name:#"NOTIFICATIONNAME" object:nil];
}
-(void)someMethod{
//something has happened, do your stuff
}
And in each of the Model class (if they are many or one dsnt matter)|
ModelClass.m
-(void)someactionHappend{
//this is your action method that you want to do stuff in the model
[[NSNotificationCenter defaultCenter] postNotificationName:#"NOTIFICATIONNAME" object:nil userInfo:nil];
}
Hope this helps you out.

Related

How to pass values to new UIViewController from UIButton of Child UICollectionViewCell in nested UICollectionViews in iOS Objective C

i have UICollectionViewController with custom UICollectionViewCell which contains child UICollectionView and custom childUICollectionViewCell.
UICollectionViewController
UICollectionViewCell (parentCell)
UILable
UICollectionView
UICollectionViewCell(childCell)
UILable (valueLable)
UIButton (Submit)
UICollectionViewCell(childCell)
UILable (valueLable)
UIButton (Submit)
i want to send text of "valueLable" on click "Submit" button to new UIViewController.
Please help me....
Create a protocol SubmissionDelegateProtocol with a method -(void) submitWithValue:(NSString*)text;
Your UICollectionViewController implements (conforms to) the protocol.
It is supposed to provide this methods for the childViews to call it.
On creating the collection views and further collection and its collection views you will have to pass down a -let's say- sumbmitDelegate property (or init parameter) that is holding a reference to the UICollectionViewController which implements the protocol.
id <SubmissionDelegateProtocol> submissionDelegate;
When you work with storyboards, then the method prepareForSegue: is the appropriate place for handing further information down to subsequent view controllers. But if both collection views use the same ViewContoller as their dataSource, as your tree suggests, then it should be even easier.
So that in the end each of the cells holds a (weak) reference to the view conotroller.
Then, upon button is pressed action, you call the delegate method
[submissionDelegate submitWithValue:[valueLable text]];
That's for the delegation pattern.
Alternatively you could work with notifications.
Try NSNotification Center
In Child View Controller, postNotifictaion
and in UIViewController listen to the same notification.
You can also pass data using NSNotificationCenter.
- (void) submitButtonClicked
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"dataFromChildToParent" object:nil userInfo: valueLable.text];
}
and In UIViewControllers ViewDidLoad Method
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loadRegistrationNotification:) name:#"dataFromChildToParent" object:nil];
and Implement the selector
- (void) loadRegistrationNotification:(NSNotification *)noti {
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"dataFromChildToParent" object:nil];
NSString *valueLabelText = [noti userInfo];
NSLog(#"Notification data %#", valueLabelText);
}
For Detailed Explanation about NSNotificationCenter see this Tutorial

multiple view controllers involved, how to send a value back to starting view controller?

I am very new to iOS and overwhelmed with resources I find online. My use case is simple
a.) ViewController parent has label called categoryLabel. A tap on label category opens a new view View 1
b.) View 1, shows all groups. Lets says A, B, C. This will be shown on table
c.) when user click on any group A, B or C, a new view View 2 appears with all categories in that group. For example, user clicks on A and on View 2 user sees categories A1, A2, A3.
d.) Now when user clicks on any specific category, how does that goes back to ViewController parent and assigns to categoryLabel?
I do not know what is the best way to approach this design.
Any guidance is very much appreciated
hope this will help
let take an example , your are going from A -> B and want send some data from B to A , there are many technique to do that but using delegate method and block are nicer way.
delegate way :-
in your B.h file
#protocol yourDelegate <NSObject>
-(void)whichCategoryClicked:(NSString *)categoryName;
#end
#interface B : UIView
#property(nonatomic, assign)id<yourDelegate> delegate;
in your B.m
just call this delegate method after Clicking particular category.
[self.delegate whichCategoryClicked :#"Category_name"];
in your A.h
assign it as delegate and import the above class
#interface A.h : UIViewController<yourDelegate>
and in Implement this method in A.m
first in your viewdidload
{
B *objB = [[B alloc]init];
objB.delegate = self;
}
-(void)whichCategoryClicked:(NSString *)categoryName
{
categoryLabel.text = categoryName;
}
You can use Local notification for this purpose names as NSNotificationCenter in iOS. Which works as follows:
To send a notification that is from the view on which you are and want to send some value from that view, use below code:
NSDictionary *dict;
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationKey" object:nil userInfo:dict];
and now on any of the view controller, you can add observer on viewDidLoad of that class as:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(methodToCall:)
name:#"NotificationKey"
object:nil];
Now call method written in above line:
- (void)updateImageFromArray:(NSNotification *)notification {
// your dict
NSDictionary *dictUserInfo = [notification userInfo];
}

Passing information back from UITableViewDataSource to ViewController

I have a ViewController with a UITableView. As I wanted to split out the data handling I created an own class that answers UITableViewDataSource.
This class is supposed to first fetch data from CoreData and afterwards from a REST API.
How can the DataSource talk back to the ViewController to tell it to call reloadData on the TableView?
What's the best practice here?
I thought about:
KVO the DataSource's data and when the array change call reloadData
Handing over a block (with [self.table reloadData]) to the DataSource which gets executed every time the data changes in the DataSource
Make the table property public on the ViewController so the DataSource could call reloadData (which I don't really like as an idea)
Have a property on the DataSource which holds the ViewController with the Table to use it as a delegate (which sounds to me like a loop)
Are there any smart ways to do it? Or even common practice how to solve this?
Update:
I'm less interested in code how to do implement a certain design pattern. I'm more interested in the reasoning why to chose one pattern over the other.
Without more details, it sounds like you need a callback here. There are several methods that will work. If you have a 1 to 1 relationship (meaning your dataSource only needs to talk to the VC), then this is a good case for either:
1.) A delegate. Create your own delegate protocol for your dataSource and then have the VC adhere to that protocol (be the delegate).
2.) Do the same thing just using a block for a callback.
KVO will work just fine as well, but the above two are more in line with your scenario.
You could add a tableView property to your custom data source but that then blurs the lines of why you created that class in the first place.
For situations like this, I prefer delegates.
#class CupcakePan;
#protocol CupcakePanDelegate <NSObject>
- (void)cupcakesAreReadyForPan:(CupcakePan *)pan;
#end
#interface CupcakePan : NSObject <UITableViewDataSource>
#property (weak) id<CupcakePanDelegate> delegate;
#end
#implementation CupcakePan
- (void)bakingComplete {
[self.delegate cupcakesAreReadyForPan:self];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [cupcakes count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return [make a CupcakeCell];
}
#end
#interface CupcakeViewController <CupcakePanDelegate>
#end
#implementation CupcakeViewController
- (void)cupcakesAreReadyForPan:(CupcakePan *)pan {
[_tableView reloadData];
}
#end
I frequently use NSNotificationCenter for these types of interactions.
In your datasource write the following code:
#define ABCNotificationName #"ABCNotificationName"
#define ABCNotificationData #"ABCNotificationData"
// ...
[[NSNotificationCenter defaultCenter] postNotificationName:ABCNotificationName object:self userInfo:#{ ABCNotificationData: data }];
In your view controller do the following:
-(void)loadView {
// setup your view
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(datasourceUpdated:) name:ABCNotificationName object:dataSource];
}
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void)dataSourceUpdated:(NSNotification*)notification {
id data = notification.userInfo[ABCNotificationData];
// respond to the event
[self.tableView reloadData];
}
Note, that if you don't have any piece of data to communicate back to the controller it becomes even easier. In your datasource write the following code:
#define ABCNotificationName #"ABCNotificationName"
// ...
[[NSNotificationCenter defaultCenter] postNotificationName:ABCNotificationName object:self];
In your view controller do the following:
-(void)loadView {
// setup your view
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(datasourceUpdated) name:ABCNotificationName object:dataSource];
}
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void)dataSourceUpdated {
// respond to the event
[self.tableView reloadData];
}

Update Image from Secondary View Controller

I need to update an image on the main view controller from a pop up view controller.
The button is called 'Feature2btn' on the Main View (EraViewController) but when I try the following code on the popup view controller it won't work.
It needs to be an immediate update as the main view is still showing in the background and does not reload so the change needs to be directly caused by the action on the pop up view.
- (IBAction)purchase:(id)sender {
HomeController = [[EraViewController alloc] initWithNibName:#"EraViewController" bundle:nil];
UIImage *image = [UIImage imageNamed:#"ico_plan.png"];
[(EraViewController*)HomeController setFeature2Btn:[feature2Btn setImage:[UIImage imageNamed:#"image.png"] forState:UIControlStateNormal];
}
There is (at least) two ways to do this:
You use a notification that one controller listens to and the other sends at the appropriate time.
You create a delegate protocol that the first controller implements and the second on calls.
The delegate one is a bit more complicated but generally considered good style. The notification one is not bad, either, but slightly less "elegant".
I will describe the notification based one here, because it seems ok for your case and would also allow to react to the purchase in multiple places by just registering for the notification there, too.
In the controller that has the image to be updated, register for a notification in viewDidAppear::
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateImage:) name:#"UpdateImageNotification" object:nil];
Implement the updateImage: method:
-(void)updateImage:(NSNotification*)note
{
NSString* newImageName = note.userInfo[#"imageFileKey"];
// ... update UI with the new image
}
Also make sure to deregister for that notification when the view goes away:
-(void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super viewWillDisappear:animated];
}
In the other controller, that triggers the update, fire the notification at the appropriate place:
-(IBAction)purchase:(id)sender
{
// ...
NSDictionary* userInfo = #{#"imageFileKey" : newImageName};
[[NSNotificationCenter defaultCenter]
postNotificationName:#"UpdateImageNotification"
object:self userInfo:userInfo];
// ...
}
The object parameter in the notification context is to be used to specify if you want to listen to the notifications by any object or just by a very specific instance. In many cases the actual instance is not relevant, but you just discern the notifications by their name (like "UpdateImageNotification" in this case).
The userInfo dictionary is intended to carry along any information you need to provide with the notification. That's why I introduced a key "imageFileKey" that is associated with the new image name.
I think you made a small mistake. But before going ahead I just want to confirm whether following is the scenario. Correct me if I am wrong
1. You have mainViewController/HomeViewController (of type EraViewController), where you want to update the image
2. On mainViewController you have a popup screen, in which above code is written. The code is intended to change the button image on mainViewController/HomeViewController
If above is the scenario then I suggest following solution.
ERROR YOU MADE
You are creating a new object of EraViewController in the code you posted above and changing the image. According to OOPS concepts, new instance of that controller will be created(a second instance which is not visible on the screen) and you are applying new image in that instance. Now as that instance is not at all visible, you get a feeling that screen is not updating.
SOLUTION
There are at-least 3 solution to this problem
1. One solution will be Daniel Schneller gave in answer (with little bit modifications probably)
2. To achieve this through the delegates.
- You have to write the protocol in the PopViewController and have to implement that in the HomeViewController/mainViewController.
- As the image changes, in PopViewController, That has to be notified to the mainViewController, using delegate and protocol method.
- So that the mainViewController will get the notification and a protocol method will be executed. In that method you should have a code to update the image on the button.
3. (This is not suggested as this is not a good design)You have maintain the instance of the actual viewController which is visible on the screen (in a variable, lets say homeViewController). Then you can use following code in popupViewController
- (IBAction)purchase:(id)sender {
UIImage *image = [UIImage imageNamed:#"ico_plan.png"];
[(EraViewController*)homeViewController setFeature2Btn:[feature2Btn setImage:[UIImage imageNamed:#"image.png"] forState:UIControlStateNormal];
}
Hoep this helps.
Thanks
Try this,
- (IBAction)purchase:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateimage" object:imageName];
}
in mainviewcontroller
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateiimage:) name:#"updateimage" object:nil];
}
- (void) updateiimage:(NSNotification *)notification
{
NSString * text =[notification object];
//update Image
}

How to pass data to parent view IOS

I know this question is asked once every two days. I can not see what I am doing wrong though.
I have a storyboard navigation controller based app.
My notification and pop / push segues works well, only thing is I can not add string to parents view NSmutablearray.
I want to add a string object to parent view's nsmutablearray. My decent code does not pass any data.
parent.h
#interface CreaatePlistTableViewController : UITableViewController<UITableViewDelegate, UITableViewDataSource>{
NSMutableArray *presenterList;
}
#property (nonatomic, strong) NSMutableArray *presenterList;
parent.m
NSString * const NOTIF_CreatePlist_UpdateTableview= #"CreatePlist/UpdateTableview";
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Private interface definitions for update tableview
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
#interface CreaatePlistTableViewController (private)
- (void)CreatePlistUpdateTableview:(NSNotification *)notif;
#end
#implementation CreaatePlistTableViewController
#synthesize presenterList=_presenterList;
- (void)viewDidLoad
{
[super viewDidLoad];
_presenterList=[[NSMutableArray alloc] init];
// Register observer to be called when logging out
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(CreatePlistUpdateTableview:)
name:NOTIF_CreatePlist_UpdateTableview object:nil];
NSLog(#"Presenter List: %#", _presenterList);
}
- (void)CreatePlistUpdateTableview:(NSNotification *)notif{
NSLog(#"Notification recieved");
NSLog(#"Presenter List: %#", _presenterList);
[_createPlistTableview reloadData];
}
child.h
#interface AddPresenterViewController : UITableViewController<UITextFieldDelegate,UIAlertViewDelegate>{
CreaatePlistTableViewController *crereaatePlistTableViewController;
}
#property(nonatomic,strong) CreaatePlistTableViewController *crereaatePlistTableViewController;
child.m
#synthesize crereaatePlistTableViewController=_crereaatePlistTableViewController;
//finished adding presenter
-(IBAction)finishedAddingPresenter:(id)sender{
//some xml string here
NSLog(#"final result XML:\n%#", writer.XMLString);
_crereaatePlistTableViewController=[[CreaatePlistTableViewController alloc]init];
//add object to parents view data source
[_crereaatePlistTableViewController.presenterList addObject:writer.XMLString];
//dismiss the view
[self.navigationController popViewControllerAnimated:YES];
//notify the parent view to update its tableview
[[NSNotificationCenter defaultCenter] postNotificationName:#"CreatePlist/UpdateTableview" object:nil];
}
Output
Notification recieved
Presenter List: (
)
So notification works when I click the button. But it does not pass object to nsmutablearray.
What I am doing wrong here ? How can I add an object to parent view's nsmutablearray?
It seems everything is good except your alloc of parent view object I am not that familiar with storyboard but You said you are using navigation navigation controller
so change this
_crereaatePlistTableViewController=[[CreaatePlistTableViewController alloc]init];
to
_crereaatePlistTableViewController= [self.navigationController.viewControllers objectAtIndex:0];
It may work I am not sure
You wrote this.
[_crereaatePlistTableViewController.presenterList addObject:writer.XMLString];
Do you ever initialize the array? No. Use the debugger and you will see that at this line the presenterList is nil.
Now as a point of style. Avoid using NSNotificationCenter to pass data or signaling other objects. #TheRonin gave a handy link. You should also look into some tutorials on Segues, because these are solved problems.
This is another related post that you might find interesting.

Resources