trying to update a UILabel on a parent view controller when dismissing the modal view - iphone

I am trying to update a UILabel in a parent View after someone makes a change in a modal view. So, after they click "save" ... the newly entered value would change what text is displayed on the parent view controller.
But, I can't seem to get that UILabel to refresh the newly entered value.
Any ideas on what I can try? I've tried a few things, but being the view is already loaded, nothing is getting "refreshed".
Thanks!

There are many ways to do this. One way is to use NSNotificationCenter to be able to do calls between different classes. So in the parent view you will have a function responsible for the update (lets call it updateLabel) and you will do the following:
- (void) updateLabel
{
yourLabel.text = #"what you need";
}
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateLabel) name:#"DoUpdateLabel" object:nil];
}
Now in other view simply post a notification in the save button:
[[NSNotificationCenter defaultCenter] postNotificationName:#"DoUpdateLabel" object:nil userInfo:nil];
EDIT:
I have to mention 2 things here:
In this scenario it is always preferable to have Shared Data Modal where you save your data in so you can access this data in any view in your program. In other words it is a good practice to separate the data from classes.
Remember to resomve the NSNotificationCenter that you used in the main view by adding [[NSNotificationCenter defaultCenter] removeObserver:self];

To elaborate on my comment. This is how I would implement a delegation method to update the label.
In the header of the parent view controller:
#import "ModalViewController.h"
#interface ViewController : UIViewController <ModalViewControllerDelegate>
/* This presents the modal view controller */
- (IBAction)buttonModalPressed:(id)sender;
#end
And in the implementation:
/* Modal view controller did save */
- (void)modalViewControllerDidSave:(ModalViewController *)viewController withText:(NSString *)text
{
NSLog(#"Update label: %#", text);
}
/* Prepare for segue */
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"modalSegue"])
{
ModalViewController *mvc = (ModalViewController *) segue.destinationViewController;
mvc.delegate = self;
}
}
/* Present modal view */
- (IBAction)buttonModalPressed:(id)sender
{
[self performSegueWithIdentifier:#"modalSegue" sender:self];
}
Here you see the delegation method in the top.
The header of the modal view controller would contain the delegation protocol like this:
#protocol ModalViewControllerDelegate;
#interface ModalViewController : UIViewController
#property (nonatomic, weak) id <ModalViewControllerDelegate> delegate;
- (IBAction)buttonSavePressed:(id)sender;
#end
#protocol ModalViewControllerDelegate <NSObject>
- (void)modalViewControllerDidSave:(ModalViewController *)viewController withText:(NSString *)text;
#end
The implementation of the modal view controller would contain a method similar to this one:
/* Save button was pressed */
- (IBAction)buttonSavePressed:(id)sender
{
if ([self.delegate respondsToSelector:#selector(modalViewControllerDidSave:withText:)])
[self.delegate modalViewControllerDidSave:self withText:#"Some text"];
[self dismissModalViewControllerAnimated:YES];
}
When the save button is pressed, the delegate is notified and the text in your text view is sent through the delegation method.

in SWIFT:
ParentViewController :
func updateLabel() {
yourLabel.text! = "what you need"
}
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.updateLabel), name: "DoUpdateLabel", object: nil)
}
In OtherView:
#IBAction func closePopUp(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("DoUpdateLabel", object: nil, userInfo: nil)
}

Related

Where to put the UIAlert? [duplicate]

I want to check the pasteboard and show an alert if it contains specific values when the view appears. I can place the code into viewDidLoad to ensure it's only invoked once, but the problem is that the alert view shows too quickly. I know I can set a timer to defer the alert's appearance, but it's not a good work-around I think.
I checked the question iOS 7 - Difference between viewDidLoad and viewDidAppear and found that there is one step for checking whether the view exists. So I wonder if there's any api for doing this?
Update: The "only once" means the lifetime of the view controller instance.
There is a standard, built-in method you can use for this.
Objective-C:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([self isBeingPresented] || [self isMovingToParentViewController]) {
// Perform an action that will only be done once
}
}
Swift 3:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if self.isBeingPresented || self.isMovingToParentViewController {
// Perform an action that will only be done once
}
}
The call to isBeingPresented is true when a view controller is first being shown as a result of being shown modally. isMovingToParentViewController is true when a view controller is first being pushed onto the navigation stack. One of the two will be true the first time the view controller appears.
No need to deal with BOOL ivars or any other trick to track the first call.
rmaddy's answers is really good but it does not solve the problem when the view controller is the root view controller of a navigation controller and all other containers that do not pass these flags to its child view controller.
So such situations i find best to use a flag and consume it later on.
#interface SomeViewController()
{
BOOL isfirstAppeareanceExecutionDone;
}
#end
#implementation SomeViewController
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if(isfirstAppeareanceExecutionDone == NO) {
// Do your stuff
isfirstAppeareanceExecutionDone = YES;
}
}
#end
If I understand your question correctly, you can simply set a BOOL variable to recognize that viewDidAppear has already been called, ex:
- (void)viewDidAppear {
if (!self.viewHasBeenSet) { // <-- BOOL default value equals NO
// Perform whatever code you'd like to perform
// the first time viewDidAppear is called
self.viewHasBeenSet = YES;
}
}
This solution will call viewDidAppear only once throughout the life cycle of the app even if you create the multiple object of the view controller this won't be called after one time. Please refer to the rmaddy's answer above
You can either perform selector in viewDidLoad or you can use dispatch_once_t in you viewDidAppear. If you find a better solution then please do share with me. This is how I do the stuff.
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(myMethod) withObject:nil afterDelay:2.0];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
static dispatch_once_t once;
dispatch_once(&once, ^{
//your stuff
[self myMethod];
});
}
By reading other comments (and based on #rmaddy 's answer), I know this is not what OP asked for, but for those who come here because of title of the question:
extension UIViewController {
var isPresentingForFirstTime: Bool {
return isBeingPresented() || isMovingToParentViewController()
}
}
UPDATE
You should use this method in viewDidAppear and viewWillAppear. (thanks to #rmaddy)
UPDATE 2
This method only works with modally presented view controllers and pushed view controllers. it's not working with a childViewController. using didMoveToParentViewController would be better with childViewControllers.
You shouldn't have issues in nested view controllers with this check
extension UIViewController {
var isPresentingForFirstTime: Bool {
if let parent = parent {
return parent.isPresentingForFirstTime
}
return isBeingPresented || isMovingFromParent
}
}
Try to set a BOOL value, when the situation happens call it.
#interface AViewController : UIViewController
#property(nonatomic) BOOL doSomeStuff;
#end
#implementation AViewController
- (void) viewWillAppear:(BOOL)animated
{
if(doSomeStuff)
{
[self doSomeStuff];
doSomeStuff = NO;
}
}
in somewhere you init AViewController instance:
AddEventViewController *ad = [AddEventViewController new];
ad.doSomeStuff = YES;
Not sure why you do this in ViewDidAppear? But if you want doSomeStuff is private and soSomeStuff was called only once, here is another solution by notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSomeStuff) name:#"do_some_stuff" object:nil];
- (void) doSomeStuff
{}
Then post when somewhere:
[[NSNotificationCenter defaultCenter] postNotificationName:#"do_some_stuff" object:nil];
swift 5
I've tried isBeingPresented() or isMovingToParent.
But It doesn't work.
So I tried below code. and It's work for me!
override func viewDidAppear(_ animated: Bool) {
if (self.isViewLoaded) {
// run only once
}
}
You can use this function in ViewDidLoad method
performSelector:withObject:afterDelay:
it will call that function after delay. so you don't have to use any custom timer object.
and For once you can use
dispatch_once DCD block.Just performSelector in the dispatch_once block it will call performSelector only once when ViewDidLoad is called
Hope it helps

How to reload another view controller's tableview data

Right now, in FirstviewController, I got a button, when i clicked it, I get the value back using delegate. Right now, I want to send this value to SecondViewcontroller and reload it's tableview data. How to do that? How about use nsnotificationcenter, but i have tried, it's not working. I post the notification in the delegate that implemented in Firstviewcontroller. the code like this:
FirstviewController.m
// delegate that get selected cat
- (void)didSelectSubCat:(SubCat *)cat;
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"DidSelectCat" object:self userInfo:#{#"catId": cat.catId}];
}
SecondViewcontroller.m
- (void)awakeFromNib
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selectedCat:) name:#"DidSelectCat" object:nil];
}
- (void)selectedCat:(NSNotification *)notif
{
NSLog(#"userinfo: %#", [notif userInfo]);
}
In SecondViewCOntroller create protocol with a methode
#protocol TeamListViewControllerDelegate <NSObject>
-(void)SecondViewController:(SecondViewController*)teamListViewController data:(NSString*)data
#end
declare delegate
#property(nonatomic,assign) id<TeamListViewControllerDelegate> delegate;
In firstViewcontroller follow that protocol and implement that method and inside that method refresh table.
I hope this will help.

Call [tableView reloadData]; on a viewController from a modalViewController

I have a modalViewController that comes up over the top of a viewController with a tableView. When the user clicks a button on the modalViewController I want to reload the tableView within the viewController with this:
[tableView1 reloadData];
I do not want to put the reload in the viewDidAppear or viewWillAppear methods as they get called when i do not need the tableView to reload (i.e. when the user clicks the back button to return to the tableView).
Is there a way to do this?
Try
1) write one method which reloads the table data.
2) Call it on the back button clicked.
This is the classic delegate pattern problem, in your modal view controller you need a delegate reference to the current view controller presenting it
//Modal
#protocol ModalVCDelegate
- (void)tappedBackButton;
#end
#class ModalVC: UIViewController
#property id<ModalVCDelegate> delegate;
#end
#implementation
- (void)backButtonTapped:(id)sender
{
if (self.delegate)
[self.delegate tappedBackButton];
}
#end
Now, in your presenting VC, just process this delegate message
//Parent VC
- (void)showModal
{
ModalVC *vc = [ModalVC new];
vc.delegate = self;
//push
}
- (void)tappedBackButton
{
[self.tableView reloadData];
//close modal
}
You can use delegate . If find it more harder then alternative is to use NSNotificationCenter. You can see accepted answer for Refreshing TableView. This is really very short, easy and understandable way.
using Notification like bellow Method:-
Create NSNotificationCenter at yourViewController's ViewdidLoad Mehod
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(ReloadDataFunction:)
name:#"refresh"
object:nil];
[super viewDidLoad];
}
-(void)ReloadDataFunction:(NSNotification *)notification {
[yourTableView reloadData];
}
Now you can Call this Notification from your modelViewController BackButton or else you want from calling this Refresh notification like putting this line of code:-
[[NSNotificationCenter defaultCenter] postNotificationName:#"refresh" object:self];
NOTE: postNotificationName:#"refresh" this is a key of particular Notification
Try to use this one
Make a Button and click on this button and than you can reload your data.
This button make custom and use it on background.
- (IBAction)reloadData:(id)sender
{
[tblView reloadData];
}
You can use NSNotification to refresh table on ViewController.
Inside viewController :
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
Write code in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadMainTable:)
name:#"ReloadTable"
object:nil];
- (void) reloadMainTable:(NSNotification *) notification
{
[tableView reload];
}
Inside ModelViewController:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ReloadTable"
object:nil];
Here you can also send custom object instead of nil parameter. But be care full about removal of NSNotification observer.

it possible to Pass Data with popViewControllerAnimated?

I came across an interesting problem, i have main ViewController let's call him MainVC with navigationController and i am doing performSegueWithIdentifier from him to Mine second ViewController let's call him SecVC. so when i am trying to do the popViewControllerAnimated i want to pass some data from the SecVC to the MainVc.. i know i can do it with appDelegate Param or with singletons class but my question is : can i do it with more Elegant solution? like i use prepareForSegue and use local parmeters..
Thank you...
The best way to do it is by using a delegate.
//SecVCDelegate.h
#import <Foundation/Foundation.h>
#protocol SecVSDelegate <NSObject>
#optional
- (void)secVCDidDismisWithData:(NSObject*)data;
#end
//SecVC.h
#import <UIKit/UIKit.h>
#import "SecVSDelegate.h"
#interface SecVC : UIViewController
/** Returns the delegate */
#property (nonatomic, assign) id<SecVSDelegate> delegate;
#end
//SecVC.M
...
- (void) dealloc
{
...
delegate = nil
...
}
When ever you popViewControllerAnimated, right after it (or before it) you do this
if(_delegate && [_delegate respondsToSelector:#selector(secVCDidDismisWithData:)])
{
[_delegate secVCDidDismisWithData:myDataObject];
}
And in the MainVC you must be certain that you implement the delegate function
//MainVC.m
- (void)secVCDidDismisWithData
{
//do whatever you want with the data
}
To avoid any warnings you must tell that the MainVC class implements the delegate like this:
//MainVC.h
#import "SecVCDelegate.h"
...
#interface MainVC : UIViewController <SecVCDelegate>
...
secVCInstance.delegate = self;
[self.navigationController pushViewController:secVCInstance];
...
While I agree that the best option is to use Delegate,
but still if any one is looking for something different, he can use NSNotificationCenter as an alternative.
In viewDidLoad of MainVC:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(recvData:)
name:#"SecVCPopped"
object:nil];
}
And add method recvData in MainVC.m
- (void) recvData:(NSNotification *) notification
{
NSDictionary* userInfo = notification.userInfo;
int messageTotal = [[userInfo objectForKey:#"total"] intValue];
NSLog (#"Successfully received data from notification! %i", messageTotal);
}
Now in SecVC, before popping, add this line
NSMutableDictionary* userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:[NSNumber numberWithInt:messageTotal] forKey:#"total"];
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"SecVCPopped" object:self userInfo:userInfo];
I would do it in one of the following ways, but I'm not sure if it's elegant enough...
In SecVC, add an #property MainVC *mainVC; Use [self.mainVC setSomeValue:...]; before calling [self.navigationController popViewControllerAnimated:...];
Use [self.navigationController viewControllers]; to find out the MainVC *mainVC, and call [mainVC setSomeValue:...]; before the line of code that pop the ViewController.
Is this what you want?
I simply set up a protocol in the view being dismissed (example in Swift):
protocol ExampleTableViewControllerDismissDelegate {
func didDismiss(withData: Any)
}
var delegate: SearchableTableViewControllerDismissDelegate?
You can then call this when you dismiss/pop your view like this
self.navigationController?.popViewController(animated: true)
delegate?.didDismiss(withData: Any)
Then in the view being dismissed to (the parent in the hierarchy), we can conform to the delegate and essentially get a callback with the data after the view has been dismissed.
//MARK: ExampleTableViewControllerDismissDelegate
func didDismiss(withData: Any) {
//do some funky stuff
}
And don't forget to subscribe to the delegate in the parent view by using
viewController.delegate = self
There is another way to pass data between views including popViewControllerAnimated and it's with a global var instance, so If you modify that Var in your detail view and after do the popViewControllerAnimated, you can call the new data in the viewWillAppear method.
The first step is declare the Global var in main.h
NSMutableArray * layerList;
And now you have to call it in detail view.
SecondView.m
extern NSString *layerList;
SecondView.h
-(void)back{
layerList = #"Value to send";
[self.navigationController popViewControllerAnimated:YES];
}
Now you can use the information in the Master View after detect the pop action.
FirstView.m
extern NSString *layerList;
FirstView.h
-(void)viewWillAppear:(BOOL)animated{
NSLog(#"This is what I received: %#",layerList);
}

how to get textfield value of second modal view to the textfield of first modal view [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Passing Data between View Controllers
I have 2 modal views. The first modal view is used to edit quantity and price; the second modal view is used when we click the price textfield of first modal view in order to give give reason why we change the price and we can put new price in price textfield of modal view. I want the price in first modal view change when I set the price in second modal view. How to catch the value of second modal view to put in first modal view ?
Use NSNotification center
You have to addobserver event in First Modalview
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reload:) name:#"refresh" object:nil];
}
- (void)reload:(NSNotification *)notification {
textfield.text= [[notification userInfo] valueForKey:#"price"] ;
}
In second modalview you have to post notification after you complete edit
(pass your textfield value)
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:#"333" forKey:#"price"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"refresh" object:nil userInfo:userInfo]
;
Finally remove observer
-
(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"refresh" object:nil];
}
You can used Singleton just to save some data on that class
The following simple steps will make you able to do this.
In the first Modal ViewController, you can declare a function like
- (void) setUpdatedValueWithValue: (NSString *) newValue
{
self.objTextField.text = newValue;
}
Declare this function on the header file too, so that we can access it from the other class.
In the second Modal ViewController
SecondViewController.h
#interface SecondViewController : UIViewController
{
id objFirstViewController;
}
#property (nonatomic, retain) id objFirstViewController;
#end
SecondViewController.m
#implementation SecondViewController
#synthesize objFirstViewController;
#end
Before you present the SecondViewController pass the object of FirstViewController to SecondViewController like,
- (void) presentSecondViewController
{
SecondViewController *objSecondViewController = [[SecondViewController alloc] init];
objSecondViewController.objFirstViewController = self;
[self presentModalViewController: objSecondViewController animated: YES];
[objSecondViewController release];
objSecondViewController = nil;
}
Then, in the function you are calling to dismiss the SecondViewController after the value edit you can do like,
- (void) finishEdit
{
if([objFirstViewController respondsToSelector: #selector(setUpdatedValueWithValue:)])
{
[objFirstViewController performSelector: #selector(setUpdatedValueWithValue:) withObject: editedTextView.text];
}
[self dismissModalViewControllerAnimated: YES];
}
Hope this helps.
set you object with your keyword
[[NSUserDefaults standardUserDefaults]setObject:#"value of second modal view" forKey:#"keyName"];
than get that object in first modal view
NSString *name = [[NSUserDefaults standardUserDefaults]objectForKey:#"keyName"];