I have created an UIView in my iPhone app. I want to handle something when user closes or opens when UIView is present as current screen. I thought, i can do this under viewWillAppear:. But, viewWillAppear: is not called in UIView. Does it work only on UIViewController? How can i handle viewWillAppear: or viewDidAppear: for an UIView?
Update: UIView what I created everything through program, not in .xib.
Please advise.
Thanks!
From your message I infer that you wrote your viewWillAppear: method on the UIView class. As you suspect, that method is part of [UIViewController]1, not [UIView]2 therefore it only gets called on the UIViewController.
You should connect the property view of the UIViewController to the UIView object in the interface builder and then implement that method in the UIViewController.
If your view is created in response to an user action,
Update for your update:
You should tag the views either in code (view.tag=1) or IB.
Then you can do if (self.window.rootViewController.view.tag == 1) { ... } from your delegate (assuming you are looking for the view of the controller who is the rootController, otherwise post more details).
It's even better if you define constants on one place instead writing 1 as a literal.
These delegate methods are called every time the superview is presented to the screen and should be implemented in the UIViewControllers.
The gotcha is that these methods aren't called when subviews are presented on the screen, so your superview-view-controller will have to respond to these events accordingly.
You can find more information in this post here.
If you study the documentation for UIView and UIViewController what you will find is -(void)viewWillAppear:animated: is a method of UIViewController and not of UIView, so in order to use it, it must be implemented by subclassing UIViewController. Generally for best practice if you want to follow MVC, any functionality that does not pertain to the view itself should be delegated to the view controller and not be in the body of your UIView subclass.
Create a new view controller with xib file, and then link your custom view class to the view in your xib file.
Related
I need to create a class that will present a UIVIew and has a some code to initialize it before it is ready to show. I need to know when the view is ready, I mean, I need something like viewDidLoad or viewWillAppear, but as it is a UIVIew it lacks these protocols.
I cannot implement it as a UIViewController as I don't want to present it modal. It is really a rectangular view that needs to show on a screen side.
How do I declare this class? If the class is a UIView based I don't have the methods I mentioned.
thanks
Any reason to not do that kind of stuff inside the initWithFrame method on a UIView? Also, you can do additional stuff on layoutSubviews. A view controller has viewDidLoad because the view is lazy loaded (from a nib or otherwise - it also has a loadView). It has viewWillAppear and viewWillDisappear because it is managing the view (btw, even the view controller is managed by another view controller - these methods are called when you have the controller within a UINavigationController or UITabBarController or such classes which mange UIViewControllers. - the view itself is not really managing anything. All it knows about is how to draw itself. For that, you have layoutSubViews, drawRect, etc.
Doing some heavy stuff upon view's load will definitely kill the UI performance. You probably need to implement another kind of design pattern that will asynchrounously assign data values to the instance of your custom view - when that is done, you call layoutSubviews or setNeedsDisplay to update the view.
The scenario you described is no reason for not implementing a UIViewController. Assume you have a container view A and a subview B. Both have their own UIViewController AC and BC. Now on AC you add the View B managed by BC:
[self.view addSubview:BC.view];
You probably want to be using UIViewController.
You don't just have to present them modally. You can get your view controller's view with yourViewController.view, and add that as a subview of whatever view you want.
If you're targeting iOS 5, there are a few new methods (such as addChildViewController:) designed to make doing things like this easier. You can do it on iOS 4 too though, and it'll still work.
Implement a drawRect in youR UIView, plus an initialization flag. Just before the view is to be displayed the drawRect will be called. If the initialization flag isn't yet set, do your initialization and set the flag. This will only look good if your initialization can be done quickly (no long synchronous calls).
I need to create a class that will present a UIVIew and has a some code to initialize it before it is ready to show. I need to know when the view is ready, I mean, I need something like viewDidLoad or viewWillAppear, but as it is a UIVIew it lacks these protocols.
You might want to rethink how your view is being used. It sounds like you're trying to put too much controller-like logic into your view. That's why you're wanting your view to behave like a controller.
More specifically: What exactly are you trying to accomplish? If you're waiting for data to load before display the view, that might actually be something to put in the controller that is calling the view.
To illustrate my point, imagine you're putting some text in a UILabel that you read from disk. The reading from disk isn't really related to the view. The view only cares what text it displays, not how it received the text. Once it's read from disk, you can create a UILabel with that text that you read. This allows the UILabel to be more flexible.
That example might not be at all related to what you're doing, but I use it as an example of the difference between a view and a controller. Anything not related with the display and drawing of the view shouldn't belong there.
I created an app in where i have appdelegate class only. I dont create a UIView or a view controller. I just added textFields, labels and buttons in my window.
I want to hide the keyboard when the user taps out of the text boxes. This is normally achieved by the method touchesBegan. For implementing this method, two things we need to do.
One is add in the header file(.h)
Another one is set the textField's delegate. Thats is, textField.delegate=self;
I did those two things. But still the touchesBegan method did not called.
Why? Should i have a UIView or UIViewController class for it?
Thanks in Advance
UIWindow is a subclass of UIView, so you could just hook into those. But this is a seriously bad design pattern. Consider modifying your application so it uses a single view controller, rather than doing everything in the app delegate. Otherwise you're jury-rigging the app delegate to be something it was never designed to do.
touchesBegan is a UIView method. if you want to do it in your setup you should create a UIView subclass, put all your controls in that one and implement touchesBegan in the UIView subclass.
EDIT 2: I now think the best soluton is to create ListeningView.h that just includes a ListeningView protocol, instead of subclassing ListeningView (since we can't do multiple inheritance in Obj-C). Then, you still need ListeningViewController as well.
EDIT: Ok, I figured out what the approved idiom is here. I should subclass UIViewController to create ResponderViewController, which will loop through its subviews for ResponderViews when it appears/disappears. Then, any viewController that has responderViews should inherit from ResponderViewController.
=======
UIViewControllers have viewWillAppear, viewDidDisappear, etc. delegate methods.
I would like to create a UIView subclass that can be added to a viewController's view, and when that UIViewController apears or disappears, a delegate function is called.
I could easily do this by putting function calls in the UIViewController viewWillAppear/viewWillDisappear delegate functions, but how can I encapsulate this behavior in the UIView?
I wouldn't do that if I were you. All that sort of behavior should not be controlled by a view; that's just was controllers are for.
I have a relatively simple app. I have a fullscreen UIView subclass (musicGridView) that the user interacts with, and a UIScrollView subclass (scrollView) that loads this class in, as well as a ViewController and mainWindow.xib.
Right now, I have a viewController instance loaded in the nib, which is of type myViewController. The nib also has instance of myUIView, which the myViewController is pointed to.
In AppDelegate, I do this:
[window addSubview:scrollView];
[scrollView addSubview: viewController.view];
scrollView.musicGridView = (MusicGridView*) viewController.view;
which I think is wrong. Ideally, the appDelegate doesn't have an instance of scrollView.
In scrollView's AwakeFromNib class, I do the initialization on the scrollView.
The way I think it should go, is load the [window addSubview:viewController.view] in appDelegate, and then point the viewController to an instance of scrollView instead of musicGridView. Then, in scrollView awakeFromNib add a subview of musicGridView.
Eventually, I want to create another view owned by scrollView that is actually a side TabBar (which isn't possible with the given API's) that the user can scroll to the left to reach.
So I guess amongst other MVC stuff, the question is, should the viewController point to the scrollView, which contains all other content UIView subclasses, including musicGridView?
It sounds like you are not using Interface Builder to design your UI. Since this is a new project, I would suggest doing that. You should not have to write any code like this at all. You will however need to describe your outlets and actions.
The two most important things you need to know when dealing with IB are the IBOutlet and IBAction keywords.
Sample class header:
#interface MyClass : NSObject {
IBOutlet UIScrollView* myScrollView;
}
- (IBAction) doWork;
#end
Now you can wire these two things up using Interface Builder by dragging sources to destinations with your mouse. YouTube has lots of tutorial videos on IB if you need a better description of how to do this.
I'm someone who doesn't use Interface Builder for UI design; in my experience, you need to go either all-IB, or all-programmatic. To solve your particular problem, I think you need your musicGridView to be an instance or extension of UIScrollView.
This can be done in your custom viewController's loadView method - simply initialize it to a UIScrollView (and add things to it), instead of a plain old UIView.
However, as described above, this approach isn't compatible with an IB-centric approach, as is confirmed in the UIViewController Class Reference
All subclasses of UIViewController
should implement the loadView method
to set the view property unless you
load your view from a nib file.
Just as a matter of curiosity, how does the nextResponder method implementation of the UIView class know who is the UIViewController that manages the view?
The UIResponder documentation states this, and I can see it work, but I don't quite understand it. To the best of my knowledge, a UIView does not maintain a reference to it's controller, so what's happening behind the scenes? Or am I just missing something obvious?
I'm still very new to Objective-C and iPhone development so I apologize if this is something obvious, but I am quite curious.
Thanks!
The responder chain is separate from the view hierarchy. The responder chain might look like this:
First Responder > View Hierarchy > Window > Window Delegate > etc...
However, objects can insert themselves into the responder chain and that's what UIViewController does. From the docs:
Because view controllers are tightly bound to the views they manage, they are also part of the responder chain used to handle events. View controllers are themselves descendants of the UIResponder class and are inserted into the responder chain between the managed view and its superview.
In Big Cocoa, this is accomplished using the -setNextResponder: method. That method isn't public in Cocoa Touch, but nevertheless that's what UIViewController appears to do.
If you look at UIView.h, you can see a member variable named _viewDelegate that is of type UIViewController*, this is probably where the view controller reference is stored when the viewcontroller's view property is set, and where it knows to look when you call nextResponder.
there is actually a private api on uiview to get the uiviewcontroller it belongs to. Very handy ;)
My guess is when a view is added to a view controller(controller.view) this property is set.
My guess is that the system may maintain a mapping between UIViewController objects and their root UIView objects. The code for traversal of responder chain may use this mapping to pass the event to the corresponding UIViewController object.
Typically subviews are added using:
subview = [viewController view]
[superview addSubview subview]
addSubview method automatically sets superview as the next responder for subview, so:
a) viewController wont have the opportunity to insert itself between superview and subview.
b) viewController does not know about superview, hence it cannot set it as its next responder.
c) Apple recommends that a view may not be shared across controllers. In absence of multi-threading this restriction makes sense only if there is a map of views and view controllers.