I have two view controllers, FirstViewController and FourthViewController. FirstViewController is my initial view controller. I have a UILabel, mainLab, in FirstViewController. I present FourthViewController with
[self presentViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"FourthViewController"] animated:YES completion:nil];
Then, I use delegates to try changing the text of mainLab in FirstViewController, after FourthViewController is pushed. This, however, isn't changing the text of mainLab. I checked and mainLab does become null after the view controller is pushed. That being said, how would I change the label's text while FourthViewController is pushed even though mainLab gets nullified by FourthViewController's pushing?
You should show some of your code. However, this is my guess that I take from similar experiences that I had before I understood the view controller's life cycle (which is quite well documented BTW).
I assume that you set the UIControls (UILabel etc) directly in your delegate methods. If that is true: Don't do hat. Do not access any IBOutlet or view item directly from without a view controller.
Instead declare a property that is an NSNumber or NSString, just well suited to carry the data that you want to set. Provide a custom setter that sets the related UILabel etc. too but only, if the it is not nil. (You may as well declare private instance variables and just provde the quasi setter method.)
In your viewDidLoad method, too, set the UILables etc. according to the values of the properties.
What happens in your case is that you set, lets say the text of a UILable before or even just after presentViewController. In your single line of code you even instanciate the view controller and then present it. Take that literally. The view controller is instanciated. Meaning a new one is created and all values get their defaults (which might be nil in some cases or empty strings or whatever has been set in the storyboard).
But even if you acces the UILabel (etc.) from the calling view controller directly after that, there is no guarantee that the newly instanciated view controller has been fully loaded in the meantime. In the event that the new view controller is loaded a bit later then your settings will be overwritten again. Unless you follow my advice.
There are other "hacks" that aim to force loading of the new view controller before actually setting its view items values. I tried one or two of them and they worked fine. But there is not guarantee that they will work in the future too and that is why I suggest to do it properly.
Related
I am trying to use a variable (birthDateLabel) in two view controllers that are separated via a modal but use the same view controller class (CreateAccountViewController). The label works in the view that it is set in but after [self dismissViewControllerAnimated:YES completion:nil]; is run the varriable is reset, how can I retain it?
Order of operations:
The first instance of CreateAccountViewController is run
A button is tapped to go to a new instance of CreateAccountViewController but this instance has a different view with a UIDatePicker to set the birthDateLabel variable.
The birthDateLabel is set
The user taps done and dismissViewControllerAnimated is run
The app updates a UILabel on the first instance of CreateAccountViewController
Step 5 is what does not work, if I put the label on the view as the picker it works but when the modal is dismissed the variable is reset. How can I keep the variable set after the modal is dismissed? Or is my only option to create separate view controller classes?
I tried to do my best explaining this, but if you need me to explain it more just comment.
The standard way to do this is to use a delegate. The fact that your two controllers are both instances of the same class doesn't make any difference, other than they will both have a variable called birthDataLabel. The value of that variable is specific to each instance, just as it would be if these were of two different classes.
So, you should do it the right way, which is to create a delegate protocol in your second instance, and have the first one set itself as the delegate when it first presents the second one.
#rdelmar provides the most technically correct answer, but if it's just one value you could simply use nsuserdefaults. If the number begins to expand creating a delegate might be a smart choice.
I am overriding the loadView method within a UIViewController as follows:
-(void)loadView
{
NSLog(#"HPSFormController loadView starting");
HPSFormView* viewForThisController = [ [ HPSFormView alloc ] initWithFrame:_frame ] ;
self.view = viewForThisController;
}
When a certain button is pressed within the view then the same UIViewController gets control again and at this point I wish to completely change the view that the controller is showing. As follows:
-(void)buttonTapped
{
ABCFormView* newview = [ [ ABCFormView alloc ] initWithFrame:_frame ] ;
self.view = newview;
}
However, the buttonTapped method does not load the second view. A completely blank view is shown instead.
How do I get the view controller to reload a completely different view when the button is pressed?
However, the buttonTapped method does not load the second view. A completely blank view is shown instead.
Is it not possible that the problem is in the way you create ABCFormView? I mean, it seems that the original view is replaced by an empty view, so check how the latter is created...
EDIT AFTER YOUR COMMENT:
if you say that the view is "created within a viewDidLoad method within the view controller", then you should instantiate your view controller:
#property (...) ABCFormViewController* newviewController;
....
-(void)buttonTapped
{
self.newviewController = [ [ ABCFormViewController alloc ] init] ;
self.view = newviewController.view;
}
keep in mind that newviewController must be around as long as you are using its controlled view, otherwise you will get a crash. That is the reason why I store its reference in a property.
Obviously you can't get your new view visible by simply setting self.view = newView; because the newView has never been added as a subview to any other views yet - i.e. not in the window.
If you need to switch to a different view, you should probably add APSFormView as a subview to your viewcontroller.view, and when you need to switch, remove APSFormView from superview then add ABCFormView as a new subview to viewcontroller.view.
If your loadView implementation needn't do much else, it may be better to use the storyboard to set it up initially. It is easy to miss, but you can specify in the storyboard that the view should be of a custom type (in the "identity inspector" with the view selected). Further, it may be worth evaluating why a completely different class of view is necessary for the same view controller instance; to me this may be a red flag regarding the application design. You may be better served by a flow between two view controllers, or else write some state-changing logic in this custom UIView-extending class. The decision for me would be made based on the model being represented by the views, along with which behaviors each is designed to facilitate.
If the models are different (i.e. your first view shows a list of accounts, second shows one account detail), or if the behaviors are significantly different (i.e. the first is viewing an account and the second is creating a new one), then I would use two distinct view controllers.
If the models and behaviors are similar, and the style should change, then I would likely write state-changing code in the custom view class to rearrange things, etc.
If you are coming from a different platform, it can seem silly at first, but we really do throw around view controllers without much hesitation. They are elegantly handled by the framework, and are designed to manage "a screenful of content" and be easily swapped for another screenful.
It is hard to tell without knowing what is inside ABCFormViewController. I had some timing issues once on a view controller which I just needed to generate the view because I wanted to capture its content to create a pdf file (its a view that never gets displayed onscreen). In that case I needed to insert a code like this:
[newviewcontroller.view setneedsrefresh];
Before I do
otherVC.view = newviewcontroller.view;
Otherwise I get a blank page.
I believe I get this because by the time everything is sorted out ARC deallocates newviewcontroller so the view is nil. In your case this may not be the problem. Is there a reason why you need a 2nd and 3rd view controller to put into your view because a much simpler way of doing this is to just transfer control to the other view controllers via modal, pop view or a navigationController. Another more usual way is to create multiple views in your XIB and then just load it into a blank view instead of creating view controllers for each of them.
I have a simple requirement.
On Click of a + button, I am trying to add a custom view to a SplitView.
I have created a class MyCustomView which is a subclass of NSView
In the applications nib file, I have a custom view which contains the buttons etc.
Now How to allocate a new MyCustomView every time ?
Is there an example to do this?
I am hoping something like
MyCustomView *v1 = [[MyCustomView alloc] init];
..
..
[splitView addSubView:v1];
[splitView addSubView:v2];
...
Please help
It's hard to tell exactly what you're describing based on your description but let's see if I understand you. You want to add a "copy" of your custom view assembly into a split view each time "+" is clicked, right?
The absolute best way to do this is to put the custom view assembly that will be copied (the "prototype") in its own xib. For each object you want to represent, you will instantiate a new copy from the xib and give it to some owner then add it to some parent view (a split view in your case ... odd for an unlimited number of views, but I don't have enough detail to say otherwise).
So. In the modern Cocoa world, such a view assembly should likely have its own view controller (NSViewController). This makes things easier for you since the xib's File's Owner will be an instance of your MyCustomViewController, whose -view is connected to the main container view in the xib (your custom view with all its subviews) and whose -representedObject is set to whatever model object your custom view represents. Your app will then maintain a list (an array or a dictionary, perhaps) of all the view controllers for the model objects. See this SO question/answer for a run-down of how to load from nibs/xibs.
This is basically how an NSCollectionView works (though the views must all be the same size - might not work for you). The collection view corresponds to your split view in this case; NSCollectionViewItem corresponds to your MyCustomViewController (and in fact on 10.5 and above NSCollectionViewItem is a subclass of NSViewController); your custom view is the collection view item's main -view. For each model object in its collection, it instantiates an NSCollectionViewItem and loads the view prototype from a xib (ideally, but this is optional), and uses this to set the item's view, then it sets the item's represented object (the model object).
I hope this clarifies things a bit. You've got some reading to do in order to understand enough of the nuts and bolts, but if you're still stuck, you might try editing your question to clarify or opening a new, more specific question.
I'm trying to understand how these two are connected. Every time you make a UIViewController does it also automatically come with its own UIView?
Also are these from Cocoa or Objective-C?
UIViewController is a Cocoa Touch class built for the purpose of managing UIViews. It expects to have a view hierarchy, but you don't "automatically" get a view (this is slightly inaccurate; see edit below). Usually you will obtain views by calling initWithNibName on your view controller.
There is some built-in magic in Interface Builder which knows that if File's Owner is a UIViewController (or subclass), there is a property called view. That's about it.
Once you have linked a view controller and a view, the view controller does a fair amount of work for you: it registers as a responder for view touch events, registers for device rotation notifications (and handles them automatically, if you wish), helps you take care of some of the details of animation, and handles low-memory conditions semi-automatically.
Edit: correction—if you don't call initWithNibName or set the view property manually, the view property getter will invoke loadView if view is nil. The default implementation of loadView will see if you've set nibBundle and nibName and attempt to load the view from there (which is why you don't have to call initWithNibName, most of the time), but if those properties aren't set, it will instantiate a UIView object with default values. So technically, yes, it does automatically come with its own UIView, but most of the time that's of little value.
UIViewController doesn't automatically come with a view. You have to make a view in the -loadView method. By default, this loads the view from the nib file you've specified. You can also override this method to make a custom view if you prefer not to use a nib.
Also, the view is not created right when the UIViewController is created. UIViewController uses a technique known as lazy-loading to defer the creation of a view until the view is actually accessed for the first time.
I've seen a few references (eg here) in response to folks having trouble getting the keyboard to dismiss in iPhone that say "double check that the delegate is attached to file's owner.
Is this necessarily true? Or just standard practice? Can't I have other objects in my nib, such as a subclass of UIViewController, and make connections to those as I like? I'd hate to have to route everything into the object that happens to be file owner.
That said, I'm having a difficult time getting the keyboard to disappear. I know it's connected to the delegate, because I can set break points and step through the code. I can see the [theTextField resignFirstResponder] get called (and return true), but the keyboard still won't go away.
Any other suggestions?
All of the controls in a particular view are intended to talk to the View Controller that owns the nib file. Even if you have, say, a UISlider that changes the value of a UITextField, this will be handled by a method in your UIViewController subclass that gets fired when the slider's value changes and updates the text in the text field. So 9 times out of 10 your UIViewController will be the nib file's owner.
Typically the text field delegate method you want to define is textFieldShouldReturn, calling resignFirstResponder on the text field, which it sounds like you've done.
Make sure that your outlet for theTextField is connected as well. It can be nil and the runtime will treat [nil resignFirstResponder] as a noop, not as an error.