I have a tab bar app that works. Each tab is a UINavigationController whose root view is some kind of UIViewController, often a UITableViewController.
There are instances in the app where I want to display a full-screen "veil" with a message about what's happening until some operation completes. The point is to swallow up any touches on the UI that would navigate away from where the operation started.
The veil is a UIView subclass. There is one singleton instance of the class. When displayed, I insert it as a subview of the UITabBarController view. The view appears over the entire UI, tab bar included. Great!
Here's the problem. I can tap the tabs and the UI changes. What I would have expected is that my veil view would have just swallowed up the touches.
I have implemented in my veil class the various touches{Began|Ended|Moved|Canceled} methods (as do-nothing methods), but the touches are still picked up by the tab bar, and frankly by any object under whereever I happen to touch.
I've also tried overriding a number of other methods including nextResponder, hitTest:withEvent:, etc, to no avail.
I am a little stumped at this point. I'm hoping someone will have some sage advise. :-)
Thanks.
It's not safe to modify the view hierarchy of framework classes. You would be much better-served simply adding it as a subview of the window itself. As for consuming touches, if making this change doesn't work, then you should also verify that userInteractionEnabled is set to YES on the view. You should not have to actually implement any touch-related methods.
I also had this problem and came up with a hacky solution. In the init of your custom UIView class, create a dummy UIView that's impossible to hit, for example [[UIView alloc] initWithFrame:CGRectMake(-1, -1, 0, 0)]. Or actually, I think any UIView not attached to the window works. Then, in hitTest:withEvent:, have it return the dummy view for every point not in your area of interest.
Related
I have a situation here which I am trying to resolve, but it seams I am missing something.
My architecture of the application is as following:
AppDelegate (TabBarController)
Navigation Controller
Viewcontroller one
Viewcontroller two
Viewcontroller three
Viewcontroller
Since I have lot of text validations and scrolling enabled, I am using a custom uiscrollview for viewcontroller one, two and three. In the custom uiscollview I am utilizing the code from Apple which causes scrollview to scroll if the textfield is being hidden behind the keyboard. The problem which I am occuring at this point is that I have the viewcontroller one working fine, but when it comes to viewcontroller two and three, it does see's that custom view controller after debugging, and reaches the point of "setContentOffSet" but not animating the scrollview, but just displaying the keyboard.
If anyone had this issue before, I would like to see what might I be missing here?
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.
Basically i wanted to implement a popup UIView so i followed what was posted here
POP-UP UIView "IMDB App" style
This works very well. However i have one query. My main view is a tableView. so when a view is popped up i disable scrolling in the table. Now when the popup subView is removed, i need to re-enable scrolling. How do i achieve that? i can't use willRemoveFromSuperview because the popup view is loading a different NIB altogether.
Should i use Notifications?
hope i was clear with explaining the scenario.
Thanks in advance!
Feloneous Cat has the correct answer. This is the perfect use a #protocol in your popup view along with a registered delegate. Something is triggering that popup view to close. Whatever that trigger is, call the protocol and the delegate can handle the situation as needed.
Furthermore, when protocols are used correctly, your code becomes very reusable within a project as well as in other projects.
What you could do is subclass UIView and override removeFromSuperview to send a notification. I don't think there's ever a case where a view gets removed without using the removeFromSuperview method.
I have a scenario where I have:
a UINavigationController with a UIViewController (mainVC) with a UIView (mainView) containing a UIScrollView (scrollView)
a fullscreen button in the mainView, where upon tapping on it pushes a new UIViewController (fullScreenVC), with scrollView object passed by reference to it (stored with the property attributes nonatomic and assign). fullScreenVC shows the scrollView object correctly.
upon closing fullScreenVC, a NSNotification is sent to mainVC. Here lies the problem...
When fullScreenVC is removed, scrollView is no longer shown in mainVC. I can tell it still exists though, through printing out scrollView on the logs during the notification.
I would of thought that by sending and storing scrollView by reference, mainVC would still have it retained. I also tried sending scrollView back to mainVC in case it was removed, but that didn't do anything.
The only way of solving it was to add scrollView back into mainVC on the notification step. Is there something special I have to do when sending UIScrollView objects around (or more generally UIKit objects)? Or did I do something stupid somewhere?
There is no pass-by-reference. In Objective-C, we have pointers to objects, and those pointers are assigned and passed around (never the objects themselves).
Anyway, that is not relevant here. Your issue is that in UIKit, a view can only have one superview (it is a tree hierarchy). When you add a view as a subview to another view, it is automatically removed from its previous superview, if there was one. See the documentation for addSubview: here: https://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/addSubview:
Do you really need to show the same scroll view in two different screens? It seems kind of odd. You should either have two different scroll views; or, you can make sure to add it back to your "mainView" after you are done with the other one.
I have always sort of wondered when to use a UIView vs. a UIViewController on the iPhone.
I understand that you shouldn't use a UIViewController unless it's a full-screen view, but what other guidelines are there?
For example, I want to build a modal overlay - a screen that will slide into place over the current screen. If this modal overlay is full-screen, should it be a UIViewController? The last time I built something like this, I subclassed UIViewController, but now I wonder if that was correct.
From Apple's View Controller Programming Guide for iOS:
"The most important role of a view controller is to manage a hierarchy of views. Every view controller has a single root view that encloses all of the view controller’s content. To that root view, you add the views you need to display your content."
Also:
"There are two types of view controllers:
Content view controllers manage a discrete piece of your app’s content and are the main type of view controller that you create.
Container view controllers collect information from other view controllers (known as child view controllers) and present it in a way that facilitates navigation or presents the content of those view controllers differently.
Most apps are a mixture of both types of view controllers."
This is a great question.
My basic rule of thumb. Is that every major 'page' of an application gets it's own view controller. What I mean by that is that during the wire framing stage of application design, everything that exists as its own entity will eventually be managed by its own View Controller. If there is a modal screen that slides over an existing screen, I will consider that to be a separate 'page' and give it its own view controller. If there is a view that overlays and existing page (such as a loading screen or help popup.) I would treat those differently, implement them as UIView subclasses and keep the logic in that 'pages' view controller. It the popup has behavior I will communicate back to that pages View Controller using the delegate pattern.
I hope this helps. It is very much a philosophical and architectural question and much could be written about it.
I use UIViewController whenever a view is full screen and either has outlets/actions and/or subviews.
Put everything on a screen into a UIViewController until the view controller starts to have too much code, then break out the screen into multiple UIViewControllers contained by one master view controller...
To put that into context of your answer, make a view controller for that modal overlay. It will have one anyway if you are using a nav controller to present it (and you probably should).
I have a somewhat different approach:
Override UIView if you plan to do custom drawing in drawRect. Otherwise, subclass UIViewController and use [self.view addSubview: blah] to add the components of the page.
There are a few other special cases, but that handles about 95% of the situations.
(You still will often need a UIViewController with a custom UIView. But it's common to have a custom UIViewController with no corresponding custom UIView.)
Is the thing that slides in a self contained screen? I mean, does it directly interact with the parent? If so, make it a UIView, if not, probably recommend a UIViewController.
A UIView is part of the UIViewController see the view property of UIViewController for this. As you pointed out correctly UIViewController manages a complete screen and there should be only one visible UIViewController at a time. But in most cases you will have more UIViews or subclasses of UIView visible on the screen.
The example you gave would be a correct use in most cases. As you may have noticed you will get a lot of functionality when subclassing the UIViewController. Animating the appearance and dismissal of the UIViewController would be one of them.
As marcc pointed out if the thing you want to slide in is not a self contained screen you would be better off using a UIView.
As a conclusion I would say that if you want to use the functionality that comes with subclassing UIViewController than go for it make it a UIViewController. Otherwise a UIView might be better.
The itunes U Standford class has a great lecture on UIViewControllers I would recommend watching it, because it has a lot of information regarding UIViewControllers in general.
If you are familiar with the MVC pattern, then you should be able to understand the difference between UIVIew and UIViewController. To make a simple statement, UIView is for rendering UI elements on screen. UIView is the superclass of pretty much all Cocoa Touch UI elements. Those elements do not know what information they are supposed to display, what they should do when a user clicks a button, what happens when an async network request is completed and so on. UIViewController is for all that and more. The view controller is responsible for placing the UI elements in the correct locations on screen, setting the contents of the UI elements, handling button presses and other user inputs, updating the model when needed etc.
Conceptually, a single UIViewController controls the contents of the whole screen in an iPhone App and that is why it is often easy to think of things in terms of view controllers. If you need a view where the user can select ingredients for a food recipe, you'll need a UIViewController for that. I made this distinction for myself because coming from a Java background I wasn't used to the framework enforcing MVC. I would think of things in terms of UIViews, and start implementing them that way and then run into all sorts of trouble because of that. If you are going to stick to UIKit for your App, then the workflow Apple has made for you is: for each separate view in your App, create a UIViewController subclass and then use Interface Builder to place the UI elements and to create connections for buttons etc. It works wonders, saves a ton of time and lets you concentrate on making your App function well.
I use UIViewController for showing View on full Screen.
For better control on custom view I prefer subclass of UIViewController instead of UIView, earlier I was using UIView for making custom sub class.