I got a problem.
That when I initial a Controller I used initwithnib:bundle: method and then I think this controller has been loaded to the memory. So I call its method selectUnreadMessage to change its header. But I found that viewdidload was called after selectUnreadMessage.
Some one can tell me why ? Thanks.
- (void)selectUnreadSegmentedHeaderButton {
if ([YDNetworkingManager sharedNetworkingManager].badgeNumbers.count) {
for (NSNumber *unreadMessagesCount in [YDNetworkingManager sharedNetworkingManager].badgeNumbers) {
if (unreadMessagesCount.intValue > 0) {
NSInteger index = [[YDNetworkingManager sharedNetworkingManager].badgeNumbers indexOfObject:unreadMessagesCount];
self.segmentedHeader.selectedIndex = index;
[self loadTableViewAtIndex:index];
break;
}
}
}
}
The code of my private method is above. This controller has the property SegmentedHeader to switch between different kinds of messages.
This method is called After the Controller which used this method as addSubView;
viewDidLoad method is called when view of Controller is preparing to be addSubView into another view. And when view of Controller was added, viewWillAppear and viewDidAppear will be called. If view of Controller is removed from superview, viewWillDisAppear and viewDidDisAppear will be called.
And view of Controller is added again, viewDidLoad will NOT be called. Other methods will call the same as I've said.
It means when you initial a Controller, viewDidLoad was not called immediately. This method will be called later. It is lazy-loading
Hope it useful!
ViewController initialization is not creating view - viewcontroller's view is loaded lazily - when it's view is actually added to view hierarchy. So, -viewDidLoad method gets called when you access viewcontroller's .view property - thus, you may consider initializing your data structures in the init method (fetch data, allocate containers, setup default values, etc.) and later, when -viewDidLoad is called - apply you data to your views / UI.
Related
I'm calling a method from another controller, but for some reason it does not work. The NSLog works fine but myButton is not showing up.
First controller .h:
-(void) buttonChange;
First controller .m
-(void)buttonChange {
myButton.hidden=NO; //this is not getting executed
NSLog(#"it's working");
}
- (void)viewDidLoad{
myButton.hidden=YES; //initially hidden
//....other codes
}
Second controller:
FirstController *theButtonInstance = [[FirstController alloc] init];
[theButtonInstance buttonChange]; //all works fine when I call this, but button is not showing up
Place this line myButton.hidden=YES; in init method of FirstController.
You're creating your instance of FirstController, but viewDidLoad hasn't run yet. Then immediately, you're setting hidden to NO. Later, viewDidLoad will be called, and it will set hidden back to YES before the view appears. You have to wait until viewDidLoad is called before you can correctly set hidden, and it won't get called right at the creation of the controller. You could do something like:
[self performSelector:#selector(buttonChange) withObject:nil afterDelay:3.0] ;
To see your button not appear then appear 3 seconds later.
If you want to create a new instance of your FirstController (what you currently do like Undo already mentioned) you donĀ“t have to call [theButtonInstance buttonChange]; but set theButtonInstance.myButton.hidden = NO; after you created the new instance of FirstController
You can create a BOOL property in FirstViewController.h. And set that property to NO before pushing the controller. After that go to the viewDidLoad method in FirstViewController and write the if condition whether to hide or show the button based on that property. Hope that works for you. Let me know if you need code for this.
Is there any possible way to provide a view that has been loaded manually from a Xib file with parameters before viewDidLoad get's called (because I'd need these parameters already inside viewDidLoad)?
I'm loading Xib files simply with:
NSBundle.mainBundle().loadNibNamed(name, owner: owner, options: options)
And immediately after that, the viewDidLoad method gets called. There is obviously no way to override and call any initializer manually.
I'm not really sure what the options argument does. Could it be used to provide parameters to the loaded Xib's view controller?
I am not aware of method to pass options to a nib loaded manually.
The way I would go is one of the following:
have few properties in your view controller that you will use to customize the initialization. You will have to set them up before you put the view controller in the hierarchy.
have a configuration block in your view controller that you will invoke at the end of the viewDidLoad in order to fetch the configuration options you need
In both cases the entity instantiating the view controller will have to set either properties or block before pushing the view controller.
This will work because the viewDidLoad method is called just the first time the view is accessed, so you still have time between loadNibName and when the view controller's view is actually loaded.
UPDATE with code
You would have to add an instance property to
class YourViewController: UIViewController {
var setupOptions: [String: String]
...
}
and then, from the caller:
YourViewController *yourVC = NSBundle.mainBundle().loadNibNamed(name, owner: owner, options: nil)[0]
yourVC.setupOptions = ["key1" : "val1", "key2" : "val2"]
// Then push the controller to the hierarchy. Only after this the viewDidLoad is called.
// You will be then able to use the stored setupOptions for any custom initialization.
This is of course just an example. The nature of the setupOptions depends on your use cases.
There's probably a more advanced, elegant way to solve this but I managed to get it working with the somewhat inelegant method like this:
Make a static (class) var in the Xib's view controller that keeps a reference to the VC that needs to provide params to it.
Make an options VO class (or a dictionary) and create it/set the params in it before the Xib is loaded and store in a var in the class.
Access the VO in the loaded Xib's viewDidLoad from the var on the static class.
My solution was to keep your viewDidLoad method clear..
Then, after you've set your properties, call a custom method that will use these properties like so:
MyCustomController.h
- (void)setupView;
#property (strong, nonatomic) NSString *test;
MyCustomController.m
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)setupView {
// this will work perfectly because this method is being
// called AFTER self.test is set
NSLog(#"test: %#", self.test);
}
Usage:
- (void)goToNextController {
MyCustomController *controller = [[NSBundle mainBundle] loadNibNamed:#"MyCustomController" owner:self options:nil];
// viewDidLoad will be called at this point..
// so setting properties here will be useless.
controller.test = #"our custom message here";
[controller setupView];
// so after our property('s) are set.. we call the "setupView" method.
// the output would be: "test: our custom message here".
}
I have a UIViewController which creates a custom sub view. The sub view is a UIView object which has been subclassed a few times.
Within the subview class I create a custom init method:
-(id)init {
self = [super init];
if (self) {
// Init code
[self spm_correctGuessViewCustomInit];
}
return self;
}
And within this I create a button and a label. The question relates directly to the button and its target action.
What I would like is for the UIViewController to have the buttons action, not the subclasses UIView (which actually creates and holds the button).
[continueButton addTarget:self.superview action:#selector(correctGuessContinueButtonPressed) forControlEvents:UIControlEventTouchUpInside];
I pass in the target of self.superview, this appears to work correctly and the correct method is run. However, I am shown a warning in the subclass 'Undeclared selector 'correctGuessContinueButtonPressed''
So am I implementing this approach correctly? Please let me know if more information is required.
One solution would be to update your custom view's init method so it takes target and action parameters (much like the addTarget: method of the button). You could then pass these values to the button via the addTarget: call.
- (instancetype)initWithTarget:(id)target action:(SEL)action {
// your normal init code here
// use target and action to setup your button
}
Your view.superview approach will not bring you to the view controller, but to a view.
You can import the header of your implementing class to fix the warning, but I think your design should be improved. Views should work pretty much on their own and not depend on their superviews, or even worse the whole architecture of views and controllers.
I'd pass a delegate down the line that gets called when the user pressed the button, or set some blocks on the views that get called when buttons fire.
Avoid communication over several layers of abstraction.
Reference previous similar question: Calling a method in a UIViewController from a UIButton in a subview
I had to add an import to the View Controller that the method was on, within the custom UIView subclass.
With the controller property set I could set the button target as controller
Ensure that the method that was being called from the button was in the controller header file, so could be seen by the subview implementation file. Previously this was not so the subview was not to know this existed.
I have a class A which has delegates . The delegates are being implemented in another class B.
In B I have a text field , which I am trying to make hidden when the delegate is called.
- (void) didRecieveResponseDelegate : (BOOL) status{
textField.hidden = YES;
}
But the textField doesnt get hidden. I've noticed none of the view related changes work inside the delegate including removing of child view controllers. What's the problem and how do I fix it ?
EDIT : B is a child view controller of another view controller
Try this,
- (void) didRecieveResponseDelegate : (BOOL) status{
dispatch_async(dispatch_get_main_queue(), ^{
textField.hidden = YES;
});
}
Does the method get called? (Breakpoint or NSLog to prove it).
Why are you using instance variables with leading underscore? That leads to bugs and confusion as well as distrust in your code.
Is textField actually set or is it nil? NSLog to prove it.
Fix spelling errors in method names. Quite possible that didReceiveResponseDelegate is called instead of didRecieveResponseDelegate.
Make sure you don't make UI calls from a background thread.
To some this may sound like a daft question. I've searched around, and found little, mostly because I cannot find the appropriate search terms.
Here what I want to do is:
The application begins at view A.
View A launches view B, and view B launches view C.
Is their a way for view C to return directly back to A without dismissing itself and thus exposing B. For example a main menu button.
You can call popToRootViewControllerAnimated: if you have a UINavigationController. If you specify NO to animate it, then it will just jump back to the root without showing B first.
I have discovered a solution to my problem. Its a bit dirty, (and I''ll probably get shot down in flames for it) but works very well under tests and is very quick to implement. Here's how I did it.
In my app I have a Singleton class called GlobalVars (I use this for storing various global settings). This class holds a boolean called home_pressed and associated accessors (via synthesise). You could also store this value in the application delegate if you wish.
In every view controller with a main menu button, I wire the button to the homePressed IBAction method as follows. First setting the global homePressed boolean to YES, then dismissing the view controller in the usual way, but with NO animation.
-(IBAction) homePressed: (id) sender
{
[GlobalVars _instance].homePressed = YES;
[self dismissModalViewControllerAnimated: NO];
}//end homePressed
In every view controller except the main menu I implement the viewDidAppear method (which gets called when a view re-appears) as follows.
-(void) viewDidAppear: (Bool) animated
{
if ([GlobalVars _instance].homePressed == YES)
{
[self dismissModalViewController: NO];
}
else
{
//put normal view did appear code here/
}
}//end viewDidAppead
In the mainMenu view controller which is the root of the app, I set the global homePressed boolean to NO in its view did appear method as follows
-(void) viewDidAppear: (Bool) animated
{
if ([GlobalVars _instance].homePressed == YES)
{
[GlobalVars _instance].homePressed == NO;
}
else
{
//put normal view did appear code here/
}
}//end viewDidAppear
There, this enables me to go back to the root main menu of my app from any view further down the chain.
I was hoping to avoid this method, but its better than re-implementing my app which is what I'd have to do if I wanted use the UINavigationController solution.
Simple, took me 10 minutes to code in my 9 view app. :)
One final question I do have, would my solution be OK with the HIG?