I know similiar questions have been asked before, but please bear with me as I am totally new at Objective C (have good knowledge on C).
My case is that I have a tab bar controller with two windows. When I press a button in "second" I change a variable changeName. I want to use the value of changeName in my "first" view controller but stumbled on to some problems:
To reach the other viewcontroller I found this from SO (unfortunately I forgot where):
-(NSString *)newName{
// Create a UIStoryboard object of "MainStoryBoard_iPhone" named storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:(NSString *)#"MainStoryBoard_iPhone" bundle:(NSBundle *)nil];
// Create a UIViewController object of the wanted element from MainStoryBoard_iPhone
UIViewController *secondview = [storyboard instantiateViewControllerWithIdentifier:(NSString *)#"secondBoard"];
return secondview.changeName;
}
Which I have in my first.m. MainStoryBoard_iPhone is the name of the .xib/storyboard-file.
but no. Error says
Property 'changeName' not found on object of type 'UIViewController *'
In my second.h I have
#property (readwrite, retain) NSString *changeName;
and in second.m
#synthesize changeName;
All help is appreciated, but please keep in mind that I have only used OOP for two days.
Thank you all in advance
EDIT: And what input should I have here?
- (void)viewDidLoad
{
[super viewDidLoad];
mylabel.text = Name; // or [mylabel.text Name] or something?
}
You need to cast the view controller returned to your customized second view controller (as UIViewController does not have the property changeName).
So do the following :
// Create a UIViewController object of the wanted element from MainStoryBoard_iPhone
SecondViewController *secondview = (SecondViewController *)[storyboard instantiateViewControllerWithIdentifier:(NSString *)#"secondBoard"];
I assume SecondViewController is the name of your second controller.
In my case I wanted to pass a string from FirstViewController to set a text field in SecondViewController, so:
1- In SecondViewController.h
#property (strong, nonatomic) IBOutlet UITextField *someTextField;// this is linked to a UITextField
2- In FirstViewController.m:
#import "SecondViewController.h"
3- I will do this after a press of a button in the FirstViewController:
SecondViewController *SecondView = [[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
//set fields values
SecondView.someTextField.text = #"HeLLO from First";
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController: SecondView];
//show AdView
[self.navigationController presentViewController:nav animated:YES completion:nil];
4- This is it!
5- There is a case, if I'm in FirstViewController and wanted to set a string property in the SecondViewController with the same way, the string won't get the value till you press a some kinda button in SecondViewController, it won't get it like in viewDidLoad for example! don't know why.
Related
I've created a test app to debug this issue so it simply has 2 views, tabs and controllers using storyboard layout in xcode6.
I have a label on tab 1 view and when I hit tab 2, I'd like to change it, e.g. it reads 'foo' now and when I tab back it will say 'bar'.
ViewController.h
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *tab1Label;
#end
ViewController2.m
#import "ViewController.h"
...
- (void) viewDidLoad {
[super viewDidLoad];
ViewController *myVC = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
NSLog(#"%#", myVC.tab1Label.text);
myVC>tab1Label.text = #"bar";
}
It could simply be that I'm calling the other view controller in the wrong way but I'm not sure the right way. The Log output is null when picking up the current text of the label.
Thanks in advance!
UITabBarController *tabBarController = self.tabBarController;
ViewController *myVC = (ViewController *) tabBarController.viewControllers[0]; // 0 = 1st VC
Was the missing link (courtesy of #rcasey on raywenderlich.com)
I'm trying to make a button take me to a new UIViewController based on the content of a textField, but when I run it and hit the button (with the right condition in the text field to take me to the new UIViewController), the screen blacks out. This is what I wrote in my .h and .m files. Can anyone help me (Im using storyboards)
#interface ViewController : UIViewController
- (IBAction)boton:(id)sender;
#property (strong, nonatomic) IBOutlet UITextField *texto;
#end
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize texto;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)boton:(id)sender {
if ([texto.text isEqualToString:#"1"]) {
ViewController2 *vc1=[[ViewController2 alloc]init];
[self presentViewController:vc1 animated:YES completion:nil];
}
}
#end
As you say the screen is blacking out, I expect your viewController is getting initialised without a view.
To initialise with a view hierarchy from a xib(nib) file:
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
where nibName can be nil if it shares it's name with the View Controller, and nibBundle can be nil it the nib is in the main bundle.
i.e....
ViewController2 *vc2;
vc2 = [[ViewController2 alloc] initWithNibName:nil
bundle:nil];
where the xib file is named ViewController2.xib
To initialise from a storyboard:
UIStoryboard *storyboard = self.storyboard;
vc2 = [storyboard instantiateViewControllerWithIdentifier:#"ViewController2"];
(you need to set up a viewController in storyboard and give it a matching identifier)
To initialise with neither storyboard or xib, you should override your view controller's - (void)loadView, create a view and assign it to self.view.
Update
In answer to your comment - the UIStoryboard... and ViewController2 *vc2= ... code would go into your button code (in your case it you would replace / adapt the line containing vc1=.... It would look like this:
- (IBAction)boton:(id)sender {
if ([texto.text isEqualToString:#"1"]) {
ViewController2 *vc2;
UIStoryboard *storyboard = self.storyboard;
vc2 = [storyboard instantiateViewControllerWithIdentifier:#"ViewController2"];
[self presentViewController:vc2 animated:YES completion:nil];
}
You will need to have created a storyboard scene in your storyboard with a viewController whose custom class is ViewController2 and identifier is "ViewController2". The identifier name is arbitrary, but must match the identifier string you use in your code.
As you are using storyboards, an alternative way to do this is to create a modal segue from the 'ViewController' scene to a 'ViewController2' scene, give it an identifier, and use performSegueWithIdentifier in your button method.
I want to pass a id from table view (TableViewController) to another view (ViewController) after tap a table cell. I have declared a storyboard ID "ViewController" for ViewController
Here is TableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: nil];
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
viewController.id=#"test";
[self.navigationController pushViewController:viewController animated:YES];
}
Here is ViewController.h
#property (nonatomic) NSString *id;
ViewController.m
- (void)viewDidLoad{
id= [[NSString alloc] init];
}
But the statement viewController.id=#"test"; occurred the syntax error.
id is a reserved word in Objective-C. Do not name any variable or property id. Rename it to something else.
Also, your viewController variable is declared as UIViewController. There is no id property in UIViewController. Change the type of viewController to the actual type it is (ViewController?).
ViewController *viewController = (ViewController *)[storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
Check the link below, covers these basics for beginners on how to pass data between UITableViewController to another view.
Storyboards Segue Tutorial: Pass Data Between View Controllers
By the way, i don't think you can use "id" as your property name.
I'm having some trouble understanding what classes can read what variables in other classes. I've read to many different things online and cant seem to find anything solid in here. I've literally wasted the past two days trying to get my program to work but no classes can read any other classes variables. Any help will be GREATLY appreciated.
This is my ViewController.h:
#interface ViewController : UIViewController
{
#public
NSString *nameOfLabel;
}
#property (strong, nonatomic) IBOutlet UILabel *firstLabel;
- (IBAction)Switch:(id)sender;
- (IBAction)changeLabel:(UIButton *)sender;
-(NSString *) nameOfLabel;
#end
nameOfLabel is a public variable and should be able to be accessed by an outside class, right?
ViewController.m:
#import "ViewController.h"
#import "NewView.h"
#interface ViewController ()
#end
#implementation ViewController
- (IBAction)Switch:(id)sender {
NewView * new = [[NewView alloc] initWithNibName:nil bundle:nil];
[self presentViewController: new animated:YES completion:NULL];
}
- (IBAction)changeLabel:(UIButton *)sender {
nameOfLabel = #"Test Name";
_firstLabel.text = nameOfLabel;
}
-(NSString *) nameOfLabel {
return nameOfLabel;
}
#end
changeLabel button changes *firstLabel.text to "Test name".
second class is NewView.h:
#import "ViewController.h"
#interface NewView : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *secondLabel;
- (IBAction)changeSecondLabel:(UIButton *)sender;
#end
and NewView.m:
#import "NewView.h"
#interface NewView ()
#end
#implementation NewView
{
ViewController *view;
}
- (IBAction)changeSecondLabel:(UIButton *)sender {
view = [[ViewController alloc] init];
_secondLabel.text = view.nameOfLabel;
}
#end
changeSecondLabel should change secondLabel.text to nameOfLabel which is 'Test name', however, the label actually disappears which makes me think that nameOfLabel cannot be reached. Ive played around with nameOfLabel, making it a #property and then synthesising it, as well as trying putting it in { NSString *nameOfLabel; } under #implementation but I still get the same result.
This line: view = [[ViewController alloc] init]; creates a new ViewController which doesn't know anything about what you may have done to some other ViewController. In your case, it specifically doesn't know that changeLabel: was called on another ViewController before this new one ever existed.
When the second view controller (NewView) is presented, it has no reference to the first view controller (ViewController) and it's data.
Here are a couple of suggestions.
In modern Objective-C I'd recommend using properties instead of exposing a variable.
Look over the naming in general. "ViewController" is not a good name for example.
If the property is part of an internal state of the class, declare it in a class extension.
Before you present the second view controller, set a reference to the string from the first view controller.
Part of ViewController.m:
#interface ViewController ()
#property (copy,nonatomic) NSString *nameOfLabel;
#end
#implementation ViewController
- (IBAction)Switch:(id)sender {
NewView *new = [[NewView alloc] initWithNibName:nil bundle:nil];
new.secondLabel.text = self.nameOfLabel;
[self presentViewController: new animated:YES completion:NULL];
}
First of all please read about coding standards, it's not a good practice to:
Name variables like "new"
Name methods like "Switch"
Name UIViewController like "view" or "NewView"
Regarding logic:
This is all messed up here. What you actually do is you create viewController with nameOfLabel which is empty and is only changed on button press. I assume you press that button so it's changed. Then on switch action you create another viewController and present it. Then from inside that new viewController you create another new viewController which has empty nameOfLabel, get this empty value and put it inside secondLabel.
There are couple of ways you can do to change secondLabel:
Move nameOfLabel to model and read it from there when you want to change secondLabel,
Because your new viewController is child of viewController that keeps nameOfLabel you can access it by calling [[self presentingViewController] nameOfLabel] but make it property first,
Pass nameOfLabel through designated initializer.
Well, if you want a simple demonstration of access of a public ivar, the syntax is:
view->nameOfLabel;
^^
not dot-syntax:
view.nameOfLabel;
(dot-syntax just goes through accessor methods).
I've only seen a handful of warranted edge cases over the years; there's rarely, rarely ever a good reason to make an ivar public (also, protected is also rarely a good choice).
I have a button that open another viewController(familyView) when clicked.
In familyView there is another button which suppose to bring me back to the mainViewController(ViewController.xib) but I don't know how to call the main viewController.
My method to call familyView
UIViewController* familyView = [[UIViewController alloc] initWithNibName:#"familyView" bundle:[NSBundle mainBundle]];
[self.view addSubview:familyView.view];
I hope you could help on how to call the main ViewController ? do I have to use the same method to call it? like this I mean:
UIViewController* mainView = [[UIViewController alloc] initWithNibName:#"viewController" bundle:[NSBundle mainBundle]];
[self.view addSubview:mainView.view];
If yes, is there a better way to implement this? in my demo project, I'm trying to make 7 views full with data and a button to go back and forth.
EDIT:
If I use UIView would that be best for me instead of using different viewControllers with their implementations and interfaces files?
My project will have views, and each view has data on it parsed from a different html page.
There are two method that can be used.
UINavigationController
Delegates
From your question it seems that a UINavigationController is the best option but I will show you both.
UINavigationController
When you load your mainViewController from your app delegate your going to need to wrap it in a nav controller like so:
AppDelegate.h
#property (strong, nonatomic) UINavigationController *navController;
AppDelegate.m
#synthesize navController = _navController;
//in didFinishLaunchingWithOptions:
UIViewController *mainViewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:mainViewController];
self.window.rootViewController = nav1;
[self.window makeKeyAndVisible];
Now in your MainViewController you have the convince of UINavigationController.
When you want to push to a child from a parent you can simply do:
ChildViewController *child = [[ChildViewController alloc]...];
[self.navigationController pushViewController:child animated:YES];
If you in your ChildViewController and want to go back simply do:
[self.navigationController popViewControllerAnimated:YES];
This is the "Drill Down" technique.
(I know "Drill Down" has more meaning than simply that but it provides a good frame of reference.)
Delegate
Now the other method that you have is to setup delegates between the classes. So if your in childView and need to call your parent, you will have a channel to do so.
In your MainViewController.h setup it like so:
#import <UIKit/UIKit.h>
//This is our delegate
#protocol TalkToParentDelegate <NSObject>
//This is our delegate method
- (void)helloParent;
#end
#interface MainViewController : UIViewController <TalkToParentDelegate>
...
..
#end
In your MainViewController.m make sure add the delegate method.
- (void)helloParent {
NSLog(#"Hello child, let me do something here");
}
In your ChildViewController.h setup it like so:
#import <UIKit/UIKit.h>
//Add header of class where protocol was defined
#import "MainViewController.h"
#interface ChildViewController : UIViewController
//Create a property we can set to reference back to our parent
#property (strong, nonatomic) id <TalkToParentDelegate> delegate;
#end
Now, in your MainViewController.m , whenever you present your ChildViewController do this:
ChildViewController *child = [[ChildViewController alloc]...];
//Set the delegate reference to parent
child.delegate = self;
//present the view
Last but not least, no when you in your child you can call methods on your parent (MainViewController) like so:
[self.delegate helloParent];
So here are two methods that you can use.
I would like to note however, you can use these together. Say you had a UINavigationController but still needed a child to talk to its parent, you can setup a delegate so that's possible.
Hope this helps.