I'm using delegation to try to change the text from a UILabel in ViewController2.
In ViewController1.h I have:
#protocol WelcomeDelegate
-(void) updateLabelWithString:(NSString*)string;
#end
#interface ViewController1 : UIViewController
#property (weak, nonatomic) id<WelcomeDelegate>delegate;
Inside ViewController1.m:
- (void)presentWelcomeAlert {
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController2* contentVC = [storyboard instantiateViewControllerWithIdentifier:#"ViewController2ID"];
[self.delegate updateLabelWithString:[NSString stringWithFormat:#"Welcome to the 11Health Family %#! We are here to accompany and support you through the journey as an ostomate. Our new Hydration Tracker is ready to follow your hydration levels. Tap 'Continue' to begin!", [dcsContext sharedContext].activeParticipant.firstName]];
UIViewController *rootVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
[rootVC presentViewController:contentVC animated:YES completion:nil];
}
Inside ViewController2.h I have:
#import "SignInViewController.h"
#interface WelcomePopoverViewController () <UIViewControllerTransitioningDelegate>
{
SignInViewController *signInViewController;
}
In the viewDidLoad:
viewController1 = [[ViewController1 alloc] init];
[viewController1 setDelegate:self];
My method in ViewController2.m:
- (void)updateLabelWithString:(NSString *)string {
welcomeLabel.text = string;
}
My issue is that the method above isn't getting called even when I call it from the first view controller.
This is a bit messy, sorry to say.....and you are re-instantiating ViewController1 in ViewController2....
If you only need to set that label once, then ditch all the delegate/protocol stuff and just call the method directly:
- (void)presentWelcomeAlert {
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController2* contentVC = [storyboard instantiateViewControllerWithIdentifier:#"ViewController2ID"];
UIViewController *rootVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
[rootVC presentViewController:contentVC animated:YES completion:^ {
[contentVC updateLabelWithString:[NSString stringWithFormat:#"Welcome to the 11Health Family %#! We are here to accompany and support you through the journey as an ostomate. Our new Hydration Tracker is ready to follow your hydration levels. Tap 'Continue' to begin!", [dcsContext sharedContext].activeParticipant.firstName]];
}];
}
and then of cause expose that method in ViewController2.h:
-(void) updateLabelWithString:(NSString*)string;
You can directly call this method no need to create delegate.
ViewController2 *obj=[[ViewController2 alloc] init];
[obj updateLabelWithString:#"title"];
[self.navigationController pushViewController:obj animated:YES];
Related
This is a normal LoginView calling on a specific action in my app
sourceViewVontroller
if ([password length] == 0) {
loginViewController *seatView = [mainStory instantiateViewControllerWithIdentifier:#"loggingView"];
[self presentViewController:login animated:YES completion:nil];
}
it checks if user not signed in yet when he's calling this action, so it redirects him to the loginViewController, then
loginViewController
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
DestinationViewController *Dest = [mainStory instantiateViewControllerWithIdentifier:#"destView"];
[self presentViewController:Dest animated:YES completion:nil];
it goes to the destinationView that needs a login, now my problem is how to go back (dismiss not presentmodalView) to the sourceViewController, or simply, how to remove the loginViewController from the queue on success and dismiss directly to the source?
destinationViewController
// Tried to use presentView .. but i dont need to reload sourceView, just dismiss to it !
//[self presentViewController:srcView animated:YES completion:NULL];
//This is what am doing ..
[self dismissViewControllerAnimated:NO completion:nil];
Or just let me know if there is other professional way to do this login flow
I suggest for you another way:
First in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
You will check user login or not. If login you will set rootView to destinationViewController. If not set rootView to loginViewController
SampleCode:
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
if (logged) {
DestinationViewController *Dest = [mainStory instantiateViewControllerWithIdentifier:#"destView"];
self.window.rootViewController = Dest;
} else {
LoginViewController *seatView = [mainStory instantiateViewControllerWithIdentifier:#"loggingView"];
self.window.rootViewController = seatView;
}
When logout you just call notification or delegate set rootViewController of window to loginViewController.
Do not use presentViewController for bringing login controller.
Complete Process :
In Singleton class. set a global property.
#property (assign)BOOL isUserLoggedIn;
When user logs in or logs out, set this variable to true or false.
Set an enum in LoginViewController.
typedef enum {
DestViewControllerOne =1,
DestViewControllerTwo
} SignInType;
In this enum, put all your view controller where you wanna put that login check.
Set a property in login view controller to hold source controller value -
#property (nonatomic, assign) NSInteger signInType;
Set up a delegate in login controller to redirect after successfull login -
#protocol SignInProtocolDelegate <NSObject>
#optional
-(void) signInSuccess:(NSInteger) signInType;
#end
Create a property with that delegate in login controller -
#property (nonatomic, assign) NSObject<SignInProtocolDelegate>* delegate;
Now before proceeding to required view controller, perform below check -
if(![[TESingleton shareData] isUserLoggedIn]){
[self funcNavigateToSignInWithAlert:YES withSignInType:proceedToDestViewControllerOne];
}
else
{
[self proceedToDestViewControllerOne];
}
-(void)funcNavigateToSignInWithAlert:(BOOL)showAlert withSignInType:(NSInteger) signIntype
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:STORYBOARDNAME bundle:nil];
LoginViewController *viewController = (LoginViewController *)[storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
[viewController setSignInType:signIntype];
[viewController setDelegate:self];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[[SlideNavigationController sharedInstance] presentViewController:navController animated:YES completion:nil];
}
By this, it will bring login page.
Next -
After successfull login, Do this in login controller :
[self dismissViewControllerAnimated:YES completion:nil];
[[self delegate] signInSuccess:self.signInType];
Import login delegate method in source controller that we have written above -
-(void) signInSuccess:(NSInteger) signInType
{
switch (signInType)
{
case DestViewControllerOne:
[self performSelector:#selector(proceedToDestViewControllerOne) withObject:nil afterDelay:0.5];
break;
case DestViewControllerTwo:
[self performSelector:#selector(proceedToDestViewControllerTwo) withObject:nil afterDelay:0.5];
break;
default:
break;
}
}
Implement these methods in source view controller -
-(void) proceedToDestViewControllerOne
{
//Restricting navigation to signin
//Addded by vikas Jul 8,2015
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:STORYBOARDNAME bundle:nil];
DestViewcontrollerOne *objDestViewcontrollerOne = (DestViewcontrollerOne *)[storyboard instantiateViewControllerWithIdentifier:#"DestViewcontrollerOne"];
[self.navigationController objDestViewcontrollerOne animated:YES];
}
This is complete process.
Use Unwind Segues
Add a method in SourceViewController
For Swift
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
}
For Objective-C
- (IBAction)unwindToThisViewController:(UIStoryboardSegue *)unwindSegue {
}
You can come back to the SourceViewController in two ways.
Using Storyboard e.g when an UIButton action fires.
Using performSegueWithIdentifier in code.
Storyboard:
Control drag form an UIButton in DestinationViewController to Exit segue inSourceViewController
In Code:
Control drag form an UIButton in DestinationViewController to Exit segue inSourceViewController
Call performSegueWithIdentifier:#"ExitToSourceViewController" when need to come back to SourceViewController
What is the best practice to switch between UIViewControllers for iOS in Objective-C? Now I have a menu UIViewController and a game's main screen UIViewController. In the menu there's a "New game" UIButton which should switch to the game's main controller. I do it like this:
- (IBAction)newGameButtonClicked:(id)sender {
ViewController *gameViewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"GameViewController"];
[self presentViewController:gameViewController animated:NO completion:^{
// ...
}];
}
When player dies, other view controller should be showed and I do it in the similar way like this:
MenuViewController *menuViewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"MenuViewController"];
[self presentViewController:menuViewController animated:NO completion:^{
// ...
}];
Is it right or there is a better way to achieve this behavior?
Thanks in advance.
If you want to init an UIViewController from the UIStoryboard you can do it like in your example. I would just use a UINavigationController as parent UIViewController and push them to the UINavigationController.
NSString *strVC = [NSString stringWithFormat:#"%#", [MenuViewController class]];
MenuViewController *menuViewController =
[self.storyboard instantiateViewControllerWithIdentifier:strVC];
[self.navigationController pushViewController:menuViewController
animated:YES];
When using segues like #mehinger explained, use [segue destinationViewController]. But this is just needed if you want to set any variables to the UIViewController before pushing it.
If you're in a Navigation Controller:
ViewController *viewController = [[ViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
or if you just want to present a new view:
ViewController *viewController = [[ViewController alloc] init];
[self presentViewController:viewController animated:YES completion:nil];
If you want to present a new viewcontroller in the same storyboard,
In CurrentViewController.m,
import "YourViewController.h"
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
YourViewController *viewController = (YourViewController *)[storyboard instantiateViewControllerWithIdentifier:#"YourViewControllerIdentifier"];
[self presentViewController:viewController animated:YES completion:nil];
or if you are using navigation controller:
[self.navigationController pushViewController:viewController animated:YES];
To set identifier to a view controller, Open MainStoryBoard.storyboard. Select YourViewController View-> Utilities -> ShowIdentityInspector. There you can specify the identifier.
Segues are the way to do when switching between view controllers:
- (IBAction)newGameButtonClicked:(id)sender {
[self performSegueWithIdentifier:#"goToGameViewController"];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if(segue.identifier isEqualToString:#"goToGameViewController") {
GameViewController *gameViewController = [GameViewController new];
//do any other preparation you would like
}
}
In your storyboard you can then drag a segue from one view controller to the other and indicate its identifier as "goToGameViewController".
I have a problem with the implementation of FPPopover library.
I have a similar issue than on an other post : Same issue on StackOverFlow
Unfortunately, a reference to the popover controller does not fix my problem.
On my future controller, I have an UITableView dynamically loaded when the action button is clicked. This is my code:
- (IBAction)displayAlternateNicknames:(id)sender{
PXAlternativeNicknamesViewController * suggestionsVC= [[UIStoryboard storyboardWithName:[ViewsParamsSingleton sharedLoginSignUpStoryBoard] bundle:nil] instantiateViewControllerWithIdentifier:NicknameSuggestionsStoryBoardID];
suggestionsVC.title=#"Suggestions";
suggestionsVC.nicknameSuggestions=self.nicknameSuggestions;
suggestionsVC.callerVC=self;
//our popover
suggestionsPopover = [[FPPopoverController alloc] initWithViewController:suggestionsVC];
suggestionsPopover.contentSize = CGSizeMake(200,200);
//the popover will be presented from the okButton view
[suggestionsPopover presentPopoverFromView:sender];
}
Here's my implementation that's working in production. Try this out. It may just be that you haven't set all the properties correctly.
// MyViewController.m
#interface MyViewController ()
#property(nonatomic, strong) FPPopoverController *popover;
#end
...
// Instantiate popover
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
LoginViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
if (self.popover) {
[self.popover dismissPopoverAnimated:NO];
self.popover = nil;
}
self.popover = [[FPPopoverController alloc] initWithViewController:viewController];
self.popover.contentSize = viewController.size;
self.popover.border = NO;
self.popover.arrowDirection = FPPopoverNoArrow;
self.popover.tint = FPPopoverRedTint;
[self.popover presentPopoverFromView:self.loginButton];
If the problem persists, I found another answer here that might help. Essentially, you'd have to disable ARC for FPPopover. I'm not sure how to do this with Cocoapods so you may have to just copy-and-paste the library directly into your project instead. I know it's not an ideal solution, but I think it's worth a shot.
Yet another possibility is to create an instance variable of your FPPopover and reference it that way. Example:
#property (nonatomic, strong) FPPopover *popover;
...
- (FPPopover *)popover {
if(!_popover){
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
LoginViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
_popover.contentSize = viewController.size;
_popover.border = NO;
_popover.arrowDirection = FPPopoverNoArrow;
_popover.tint = FPPopoverRedTint;
}
return _popover;
}
...
-(void)buttonTapped:(UIButton *)sender{
[self.popover presentPopoverFromView:sender];
}
I have a UINavigationController and I'm trying to release from memory every UIViewController once another one is on top of the stack. I assign the viewControllers property of the UINavigationController to the new UIViewController and then pop into it. This way I always have just one UIViewController in stack. However, the memory keeps adding up every time I create a new UIViewController. Dealloc is called, but the memory usage remains the same.
You can download the example project HERE
FirstViewController.h
#import "SecondViewController.h"
#interface FirstViewController : UIViewController
-(IBAction)goToSecond:(id)sender;
#end
FirstViewController.m
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.navigationController.viewControllers);
}
-(void)goToSecond:(id)sender{
SecondViewController *secondVC = [[SecondViewController alloc]init];
[self.navigationController setViewControllers:#[secondVC]];
[self.navigationController popViewControllerAnimated:NO];
}
-(void)dealloc{
NSLog(#"FirstVC dealloc");
}
#end
SecondViewController.h
#import "FirstViewController.h"
#interface SecondViewController : UIViewController
-(IBAction)goToFirst:(id)sender;
#end
SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.navigationController.viewControllers);
}
-(void)goToFirst:(id)sender{
FirstViewController *firstVC = [[FirstViewController alloc]init];
[self.navigationController setViewControllers:#[firstVC]];
[self.navigationController popViewControllerAnimated:NO];
}
-(void)dealloc{
NSLog(#"SecondVC dealloc");
}
#end
Navigation controller should not be used as you intended.
You should call pushViewController and popViewController for present/dismiss your viewControllers.
If you have memory issues, try to release memory in didReceiveMemoryWarning callback
I'm not sure about the benefit of a uinavigationcontroller but anyway you could add this snippet on your .m of your uiviewcontrollers
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if (self.navigationController.viewControllers.count > 1) {
NSMutableArray *newViewControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[newViewControllers removeObject:[controllers objectAtIndex:1]];
self.navigationController.viewControllers = newViewControllers;
}
}
And instead of
[self.navigationController setViewControllers:#[firstVC]];
[self.navigationController popViewControllerAnimated:NO];
you can set
[self.navigationController pushViewController:[[FirstViewController alloc] init] animated:NO];
You are using pop to go further, but you need to use push if you want to go to the next ViewController.
-(void)goToSecond:(id)sender{
SecondViewController *secondVC = [[SecondViewController alloc]init];
[ self.navigationController pushViewController:secondVC animated:YES];
}
And in the SecondViewController to go back to your FirstViewController you should use pop
-(void)backToController
{
[self.navigationController popViewControllerAnimated:YES];
}
In your case
-(void)goToFirst:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
I uploaded a simple app to GitHub, with the fake navigation bar that i was talking about in my comment, hope it helps for your needs: https://github.com/yosihashamen/HelpersApps
Be ware that you must keep on "BaseViewController" alive at all times.
What I mean is something like this.
In your FirstViewController use presentViewController instead of push adding a new UINavigationController to the SecondViewController
-(void)goToSecond:(id)sender{
SecondViewController *secondVC = [[SecondViewController alloc]init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:secondVC];
[self presentViewController:nav animated:YES completion:NIL];
}
In the SecondViewController add an UIBarButtonItem to the Navigation bar
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleDone target:nil action:#selector(goToFirst:)];
self.navigationItem.backBarButtonItem = backButton;
And implement dismiss method.
-(void)goToFirst:(id)sender
{
[self dismissViewControllerAnimated:YES completion:NULL];
}
Try out setViewControllers:animated:
This allows you to explicitly set the view controllers on the UINavigationController stack, like you are doing, and it will automatically handle the navigation animation without you having to call popViewControllerAnimated:
This is useful if you have a multi-view journey where you need to get rid of the screens that have been shown so far but maintain the navigation animation (eg. app demo on launch) or if you want to easily push multiple view controllers on the navigation stack at once.
Apple doc here: https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html#jumpTo_21
I'm writing a program for fun, and in it I have a UIViewController with four buttons, they all send the user to another view, but depending on what button is pressed, I want the label in that other view to have a different text. To do that, I decided to have the label's text to be set equals to a NSString variable, and that variable is set to a certain text depending on what button is pressed. So I have this:
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"resultScreen"];
[self presentViewController:vc animated:YES completion:nil];
_myLabel.text = [NSString stringWithFormat:myText];
In which myText is the NSString variable I want the label to be set to.
Everything seems to be in order, but the label won't have it's text changed. If I try to change it's text inside the viewDidLoad function, though, it works just fine, so I don't know what is happening.
Any idea of what I might need? If you need more info, just comment what you want and I will try to provide it.
You cannot update UI of other controller from current one - you can set variables and later on in viewDidLoad or viewWillAppear you can assign those values to UI
In File: FirstViewController.m
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
SecondViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
vc.passedText = #"Hello Next";
[self.navigationController pushViewController:vc animated:YES];
In File: SecondViewController.h
#interface SecondViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *myLabel;
#property (strong, nonatomic) NSString *passedText;
In File: SecondViewController.m
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
self.myLabel.text = self.passedText;
}
I think you need to set your ViewController's property before calling the:
[self presentViewController:vc animated:YES completion:nil];
If you set it after presenting the view, you have to manually refresh the view, which is a bit of an overkill. Just set it before presenting the view. So it would look like:
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ResultScreenViewController *vc = (ResultScreenViewController *)[mainStoryboard instantiateViewControllerWithIdentifier:#"resultScreen"];
vc.customTitleLabel.text = [NSString stringWithFormat:myText];
[self presentViewController:vc animated:YES completion:nil];
In your ViewController header file:
#property (nonatomic, strong) UILabel *customTitleLabel;
First Create a subclass of a UIViewController with a custom label:
#interface NextViewController : UIViewController
#property (nonatomic, strong) UILabel* customLabel
#end
Now back to your code. You simple cast the UIViewController you get to the subclass you created so you can access the label inside you view controller.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
NextViewController* nextVC = (NextViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:#"resultScreen"];
nextVC.customLabel.text = [NSString stringWithFormat:myText];
[self presentViewController:nextVC animated:YES completion:nil];
Ok, with the help of one of the answers here I found I solution that works for me. Before, when I had
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"resultScreen"];
[self presentViewController:vc animated:YES completion:nil];
_myLabel.text = [NSString stringWithFormat:myText];
I didn't realise that the resultScreen wasn't linked to UIViewController, but to ViewContronller. So all I needed to do was change the vc declaration to
ViewController *vc = (ViewController *)[mainStoryboard instatiateViewControllerWithIdentifier:#"resultScreen:];
So then all I needed to do was change the label's text at the ViewDidLoad module. It works just fine for me now.
Its simple..
if ([_mylabel.text isEqualToString: myText)
{
//--- Now process your method here. Show alert or what else you need
}
try this. I think that accessing UIlabel directly of other UIViewController is not a good approach. Follow this & it will definitely work.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
resultScreen *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"resultScreen"];
vc.strText = myText;
[self presentViewController:vc animated:YES completion:nil];
In your resultScreen view controller create a NSString property.
resultScreen.h
#property (retain, nonatomic) NSString *strText;
resultScreen.m
-(void)viewDidLoad
{
self.myLabel.text = self.strText;
}
you are trying to set the labelText before presenting the second View.
Before view gets loaded into the memory, its all outlets are nil.
so use the NSString variable in second view, and try to set the value of that variable,and then in
viewWillAppear: method assign the value of string variable to the labelText, this will definitely work.
Cheers!