setting text of UITextField from another class - ios

I am a beginner in "iOS", as will become clear from this question!! I am writing unit tests for my iOS app. I am trying to set the UITextField from the test class. Every time I set the username textfield text from the test case it returns null. Is there no way I can do this? I don't really want to change the code in my controller class for a test!
All the examples online create an instance of a class and set the text like below except using #synthesize (which I thought wasn't needed in "iOS7"), why is it returning null?
Code in Controller.h :
#property (nonatomic, retain) IBOutlet UITextField *username;
Test Case class :
SignUpViewController *viewController = [[SignUpViewController alloc]init];
viewController.username.text = #"username#example.com";
username.text =null

Since your property is declared using IBOutlet, I assume you're using storyboards for your views. If this is the case, then it's not an issue of the textField's text being nil, it's an issue of the whole textField being nil.
When a view controller is loaded from the storyboard, all your IBOutlets (provided they're hooked up correctly), are initialized for you. When running unit tests, there is no interaction with the storyboard, so your textField will not be initialized.
To get around this issue, you can create and assign the textField yourself:
SignUpViewController *viewController = [[SignUpViewController alloc]init];
viewController.username = [[UITextField alloc] init];
viewController.username.text = #"username#example.com";
Or, even better, you could take a look at OCMock, and create and assign a mock text field in your unit tests.

You need to load the view first.
SignUpViewController *viewController = [[SignUpViewController alloc]init];
[viewController view]; // !!
viewController.username.text = #"username#example.com";

Just because a property exists, doesn't mean the instance variable that backs it has been created. That particular UITextField is a reference to an object loaded from a NIB file (see the IBOutlet attribute),so you probably want to initialize the view controller using:
NSString *nibname = #"SignUpViewController"; // JUST GUESSING!
SignUpViewController *viewController = [[SignUpViewController alloc] initWithNibName:nibname
bundle:nil];

To get around this issue, you can create and assign the view of view controller yourself in unit tests class by writing this:
let _ = self.mockSubject.view
Here mockSubject is the viewController.

Related

Where to initialize subview?

I am new to iOS development and I am currently reading the book : iOS Programming (Objective C) by Big Nerd Ranch.
I am confused as in where to initialize subviews such as UIButtons, UIImageView while creating views programtically:
Should the intialization be done in the Main UIView i.e in the
initWithFrame method and maintain a additional weak reference to the subview in the UIView.
or
should I do it in the UIViewControllers loadView method and maintain a weak reference to the subview in the uiviewcontroller (Same approach used while creating UIVew using the interface builder).
I have seen both the approaches being used in various stackoverflow posts but no post that explains which approach is the right one.
you can initialize as per your app's requirement. If any view or button or anything is part of initial setup of your app then you should initialize it in viewDidload.
Now, for example there is requirement like user press button and then new view will be created then you can initialize view in button's click method etc.
So, it's depends on your requirement.
Static views which will live from start to and of app should be initialize in viewdidload, because this is the first method getting called of viewcontroller.
hope this will help :)
It dependes on which architecture you are using. Apple raises the flag of Model-View-Controller, but in fact, UIViewControllers are the View.
For Example:
Let's say that you have a pretty LoginViewController. When you instantiate it, you will be doing something like
LoginViewController *loginVC = [[LoginViewController alloc] init];
At this point, no view is loaded. Your ViewController has just executed the init method, nothing else. When the system calls
loginVC.view
the first method to be executed will be
- (void)loadView;
there you should do exactly that, load your view. So, the approach i like is to have an additional LoginView.
- (void)loadView
{
// you should have a property #property (nonatomic, strong) LoginView *loginView;
self.loginView = [[LoginView alloc] init];
self.view = self.loginView;
}
and in the LoginView init method, you should put your code to build up the view.
However, you could eliminate LoginView, and instantiate all your subviews like this:
- (void)loadView
{
self.view = [[UIView alloc] init];
UIButton *button = [[UIButton alloc] initWithTargetBlaBlaBla...];
[self.view addSubview:button];
// add more fancy subviews
}
In my experience, the first approach is much cleaner than the second one. It also makes version control a lot easier (try to merge a xib, I dare you). I always use MyView.m to build the view (a.k.a setup constriants, style) and use MyViewController.m things like animations, lifeCycle. I like to think that MyView.m is the programatic xib, so anything that you can do with xibs, you should me able to do it inside your view.
Hope it helps!!

UITextView is nil when attempting to set for popover

I have two View Controllers: SavePopOverVC and MainVC. I also have a nib file called SavePopOver. SavePopOver has three items, a UIButton, a UIImage and a UITextView. The image and text view have outlets to property fields in SavePopOverVC called captionImage and captionTextView respectively. The button has an outlet to an IBAction in SavePopOverVC.
In MainVC.m I have the following two lines in my class extension.
SavePopOverVC *spvc;
UIPopoverController *popover;
In my viewDidLoad of the same file I have the following lines relating to my popover.
spvc = [[SavePopOverVC alloc] initWithNibNamed:#"SavePopOver" bundle:nil];
popover = [[UIPopoverController alloc] initWithContentViewController:spvc];
In my function that displays my popover, also in MainVC.m, I have the following lines.
[popover setPopoverContentSize:CGSizeMake(600,200)];
[popover presentPopoverFromRect:_header.frame inView:self.view permittedArrowDirection:UIPopoverArrowDirectionAny animated:YES];
[((SavePopOverVC *)popover.contentViewController).captionTextView setText:#"Some text here"];
However, captionTextView is nil when I make the setText: call. The app doesn't crash but the text isn't set. After the popover is displayed and I click on the UIButton to save the string typed in captionTextView I get the string just fine. So, I know the two are ultimately linked correctly, but how can I set captionTextView from when I display the popover?
If it is worth noting, I'm developing solely for iPad with this one.
It is most likely nil because its view isn't loaded at the time you set the text. Unlike most other modern languages, in Objective-C calling a method on a nil object doesn't cause an exception, it just does nothing.
To solve this, you can create a custom NSString property in your SavePopOverVC, e.g.
#property (nonatomic, copy) NSString *caption;
Before you call presentPopoverFromRect:, assign a value to this property. Inside SavePopOverVC, override viewDidLoad and set the captionTextView.text = self.caption;
There might be people who disagree with me, but I don't recommend exposing UI controls as properties in a view controller. This behaviour is one of the reasons for that.

Where to set a property in UIViewController

A rather basic question I'm unsure about. I typically set up my UIViewController's view-related code in viewDidLoad. If the controller has some properties for labels, etc, this is where I would initialize them and add them to the view. These properties are usually declared in the .m so can be considered pseudo-private.
However - if the controller exposes one of these properties (let's say a UILabel) in its header file, I am finding that I can't rely on it existing when it comes time to set it up. For example:
CustomViewController *controller = [CustomViewController alloc] initWithNibName:nil bundle:nil];
controller.someLabel.text = #"label text goes here";
//then comes the presentation code
I find that I am setting the label's text too early - viewDidLoad has not fired yet so the label is nil.
Should I create this label in init and add it in viewDidLoad? Should I be doing all my set up in init? Or maybe all the initialization of view properties? Or judge it on a case by case basis?
Or maybe the root cause is that I shouldn't have a controller exposing a view (the label) and use some other pattern?
I'm looking for a consistent way to structure my code.
Yeah, you are pretty much right already. The thing is, all views components of your controller are not loaded until the view is actually presented. So you cannot set anything of your IBOutlets from outside the controller.
One approach for passing, for example, a text for an UILabel, it's create a new string property, let's say self.myString, assign it from outside, and in your viewDidLoad, set in the labels' text this property.
CustomViewController *controller = [CustomViewController alloc] initWithNibName:nil bundle:nil];
controller.myString = #"label text goes here";
And inside the CustomViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
(...)
self.label.text = self.myString;
}
I tend to do something like the following, which works for me if I only want to update the view on demand (if I want to update it more frequently then I would do so in viewWillAppear or via KVO or some other notification mechanism).
Have some private method that does my UI setup based on the property:
- (void)_updateUIForProperty {
// Handle UI update
}
Implement a setter for my public property that calls the _updateUIForProperty method if the view has been loaded already:
- (void)setProperty:(<#property type#>)property {
_property = property;
if(self.isViewLoaded) {
[self _updateUIForProperty];
}
}
And then to handle the case where the property was set prior to the view loading, we do something like this in viewDidLoad:
- (void)viewDidLoad {
// Other initialization
if(_property != nil) {
[self _updateUIForProperty];
}
}

How to ensure a UIView has loaded?

This may sound silly, but read on...
I want to set the text of a UILabel from outside of a UIViewController that is instantiated by a storyboard. I need to make sure that the label property of the view controller is set when I set its text otherwise the label's text won't be set(because it won't be loaded yet to receive a text value).
Here's my current solution:
// Show pin entry
if (!self.pinViewController) {
// Load pin view controller
self.pinViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"pinScreen"];
self.pinViewController.delegate = self;
if (!self.pinViewController.view) {
// Wait for pin screen to fully load
}
[self.pinViewController setMessageText:#"Set a pin for this device"];
}
Initially I had a while loop that looped until the value of view was not nil, But it seems the very act of checking the view loads it(as mentioned here: http://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006926-CH3-SW37)
I tried using the isViewLoaded method with no success. It just looped forever.
I've gone forward with the above code as my current solution, but it feels wrong.
Is there a better way ensure a UIView has loaded?
I want to propose an alternative way where you don't have to rely on the availability of the view.
If you need to wait for the view to load before you can call other methods on your viewController you break encapsulation, because the viewController that calls your PinViewController has to know about the inner workings of your PinViewController. That's usually not a good idea.
But you could save objects like NSStrings in the PinViewController instance, and when the view of the PinViewController will appear you set its views according to the properties you have set before.
If you need to change the text of an label from outside your viewController you can also create a custom setter that sets the label.text for you.
Your .h
#interface PinViewController : UIViewController
#property (copy, nonatomic) NSString *messageText;
// ...
#end
And your .m
#implementation PinViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.messageLabel.text = self.messageText;
}
// optional, if you want to change the message text from another viewController:
- (void)setMessageText:(NSString *)messageText {
_messageText = messageText;
self.messageLabel.text = messageText;
}
// ...
#end
viewDidLoad should solve this I guess.
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html
I would rather see you change your logic and do it the way that #MatthiasBauch shows in his answer. However, to answer your actual question, you can simply set a view property in order to force it to load:
self.pinViewController.view.hidden = NO;

how do i carry on values from one ViewController to another, which are user input and 'calculated'

I want to carry out the 'TotalLabel' Calculation, onto another ViewContoller known as 'AdultPayNowViewController'. I also want to carry out the 'x' value, which is a UITextField that the user inputs a quantity onto 'AdultPayNowViewController'
//AdultTicketCalculator.h
#interface StudentTicketCalculatorViewController : UIViewController {
IBOutlet UITextField *Quantity;
IBOutlet UILabel *Pricelabel;
IBOutlet UILabel *TotalLabel;
IBOutlet UIButton *CheckoutButton;
}
-(IBAction)calculate;
-(IBAction)clear;
#end
\\AdultTicketCalculator.m
-(IBAction)calculate {
float x = ([Quantity.text floatValue]);
float c = x*15;
TotalLabel.text = [[NSString alloc] initWithFormat:#"$%.2f", c];
}
-(IBAction)clear {
Quantity.text =#"";
Pricelabel.text =#"$15.00";
TotalLabel.text =#"";
First off, these values probably shouldn't even live in a controller. If they're really values, they ought to be a part of a model that is shared by or passed between controllers in the usual MVC manner.
But moving on, there are really only a few ways to pass values between controllers (or any object, for that matter). Most straight forward is, you create a custom initializer in AdultPayNowViewController called -initWithTotalLabel:andXValue: and created that controller within StudentTicketCalculatorViewController like so:
[[AdultPayNowViewController alloc] initWithTotalLabel:totalLabel andXValue:x];
Or from some shared parent like:
[[AdultPayNowViewController alloc] initWithTotalLabel:[studentController totalLabel] andXValue:[studientController x]];
Likewise, if you don't need those values for the adult controller's initialization, you could always make them properties and set them directly:
adultController = [[AdultPayNowViewController alloc] init];
[adultController setTotalLabel:[childController TotalLabel]];
[adiltController setXValue:[childController x]];
Another possibility would be to make the student controller a delegate of the adult controller, and make the adult controller ask its delegate for the values of totalLabel and x when it needs them.
Finally, you could pass student controller as a parameter or user data to a method or notification responsible for creating adult controller, and then use its properties to either set or initialize adult controller with the desired values. This is more or less the approved "Storyboard" way of doing things, I believe, with everything going down in -prepareForSegue:sender:.

Resources