I have been using this:
UIApplication.sharedApplication().keyWindow?.rootViewController!.view.addSubview(self.customView)
To add a modal view over all the views in the view hierarchy in app.
But this is giving me problem. It adds subview as expected sometimes but sometimes it doesn't' work.
In what case this wont work and what's the best way to add modal view in view hierarchy like UIAlertView.
Depending on what's happening, there can be more than one UIWindow at a time - for example, if a system alert shows up, you will have two separate windows (one for your view controller, one for the alert itself).
A similar example can be made for the system keyboard. If the keyboard has focus, that will be your keyWindow.
A way of making sure you are adding the subview on top of all windows, could be: UIApplication.sharedApplication().windows.last?.addSubview(yourSubView).
I've also seen people using the application delegate, with: UIApplication.sharedApplication().delegate?.window.
Related
(I have read other questions and answers on this topic, but most are very old and do not relate to iOS 9 or 10.)
The app design calls for the top half of the display to always contain the same content. (An image being edited by the user.)
The bottom half of the display needs a UITableView. When a UITableViewCell is tapped, the bottom section needs to transition to a new UIViewController with slide-on animation, similar to how UINavigationController push segues work.
Problem: only the bottom view needs to transition to the new view controller(s), and back again. The upper half of the view hierarchy needs to remain unaffected. For this reason, I can't place everything inside a UINavigationController, and I can't have a UINavigationBar at the top of the screen.
Question: what approach should I take in such a situation, where I need only one UIView hierarchy to transition in push-segue fashion, but not anything else? Thanks.
Edited with Solution
Solution follows, for those following along at home.
Yes, you can actually use a UINavigationController for the bottom half.
If you are using Storyboards, the easiest way to do this is to use a container view for each part of the screen which you then can embed a UIViewController in for the top part and a UINavigationController in for the bottom part. If you are doing this programmatically, just add the view controllers as child view controllers to your app's initial view controller (see this answer for more info) which is essentially what the Storyboard will do for you automatically when using a container view.
As a child view controller, the UINavigationController will act independently from the top UIViewController and should behave as expected.
I recommend the programatic approach for the following reasons:
It helps you understand the inner workings of child/parent view controllers much better which will likely save you a significant amount of debugging time down the line.
It makes adding/removing/swapping child view controllers as simple as a few lines of code. Trying to do this with Storyboards is notoriously hacky and cumbersome.
It's much easier to keep track of changes using GIT (most mid-size/larger companies actually prohibit Storyboards for this very reason)
If you want change in part of the screen you can use container view. For details refer Swift - How to link two view controllers into one container view and switch between them using segmented control?
You can use multiple view in one view controller and can give animation like push or pop to show or hide it.
Second approach is you can use Container View which will give exact effect like navigation stack.
I'm using LNPopupController (https://github.com/LeoNatan/LNPopupController) in my project. I also have an UISearchController for my Table View.
But when I imported the LNPopupController in the project, the UIKeyboard on the Search Controller is not clickable. As soon as I click on a letter on the keyboard, the search controller just gets dismissed. Please let me know the reason for this bug.
The issue has been fixed. The issue was, a view was added to an internal controller of Apple, which caused them to return an incorrect tappable view on the keyboard. I changed the logic to more correctly only load the helper view if needed and not necessarily on first layout.
I looked at the sample code you provided.
Here's what's going on.
LNPopupController has categories that swizzle (override) methods in UITabViewController and UIViewController, which affect your view controllers when they load.
The effect you're seeing when the search is active is the same behavior if you clicked the dimmed background (of the tableView above the keyboard) -- the search is cancelled.
In other words, the touch events aren't being handled by the keyboard overlay, but by your view controller, as if the keyboard overlay wasn't even present.
If you want to use the LNPopupController project, you should open an issue on GitHub so the maintainer can fix it.
There's a particular control which I'm trying to build properly. I refer to it as an ImageTile. It's basically a little square box, which, when the user taps it, will present the user (via an action sheet in a popover) the option of selecting an image from the library, or taking a photo. Depending on the response, I then either present the UIImagePickerController inside a popover (for selecting an image) or modally (for taking a new picture). Once they take/select the image, I have a modal view which appears and allows them to edit the picture in a few simple ways. When they finish editing, the modal dismisses, and the original ImageTile, rather than being a blank square box, gets filled up with the user's edited image.
The issue is that this ImageTile control is going to be used profusely throughout several different parts of the application, across numerous View Controller hierarchies, and so on... and I really want it to be a basically totally self-contained unit, such that whenever I stick an ImageTile inside a UIView onscreen, all the above functionality is handled by the ImageTile itself.
Initially, I made it a UIViewController subclass (so it could present modals etc), and just added its view as a subview of a "holder" view onscreen. I know this isn't recommended, as the controller isn't part of the VC hierarchy then... and also, I wound up with some really weird behavior regarding things like autorotation, especially when the camera was involved.
What's the "right" way to implement something like this?
I think what you've done by making it a UIViewController subclass is correct. You should just use the methods that UIViewController exposes for adding child view controllers, such as - addChildViewController:.
You will also note that Interface Builder has a Container View object designed specifically for holding a place in the hierarchy for a child View Controller:
I am subclassing UIApplication to intercept and display touches in my TouchDisplay view. I would like to extend the Application, Window, Delegate or Main ViewController in order to keep my TouchDisplay view on top of all other views. As my and most other applications work, views and controllers are added and removed all the time. I figure the correct answer will be able to deal with these additions and removals and stil keep the TouchDisplay view on top.
Thanks for your help,
Joe
Here are a few approaches you could take for this:
If you're targeting iOS 5+ and iPad only, you can make a top-level view controller which has two contained view controllers. The first would be a view controller for your "TouchDisplay" view. The second would be the application's normal root view controller. (i.e. your existing main view controller; you'll need to set definesPresentationContext to YES on this view controller) Since you're writing the container view controller, you can order those two subviews however you like. There is a WWDC 2011 Talk on view controller containment that goes into great detail about this. This is the most "correct" approach IMHO, because it gives you a view controller for your TouchDisplay view, handles rotation and generally plays nice with others. (This only works on iPad, because on iPhone a new modal view always covers the full screen.)
A more straight-forward approach is to simply add your TouchView to your existing top-level UIWindow as a subview with addSubview:. Most applications don't actually remove the top-level view controller or add new top-level ones; they just present other view controllers from it. A view you add in the top-level window will stay above those. Of course, your app may not follow this rule, in which case you might try option #3 instead. This has rotation gotchas (your view will not auto-rotate when the device rotates, so you need to do this yourself.) You could also force your view back to top, say, on a 1-second timer, if you are having issues with other things covering it. This is also not as nice as option #1 because you don't get a UIViewController, just a UIView.
The most extreme approach is that you can create another UIWindow and give it a higher window level, such as UIWindowLevelAlert and put your TouchDisplay view in that. You can then make the window background transparent, and it will stay above your normal app content. There are lots of gotchas here, especially about auto-rotation and which window is the keyWindow (which is why you should use #1 or #2 instead if you can).
After some time I was able to get my app working. I have made an easy to use overlay that shows touch feedback over your existing application.
You can download the project here:
https://github.com/megaplow/FingerTracks/tree/master/FingerTracks
Happy coding,
Joe
I am subclassing UIApplication to intercept and display touches in my TouchDisplay view. I would like to extend the Application, Window, Delegate or Main ViewController in order to keep my TouchDisplay view on top of all other views. As my and most other applications work, views and controllers are added and removed all the time. I figure the correct answer will be able to deal with these additions and removals and stil keep the TouchDisplay view on top.
Thanks for your help,
Joe
Here are a few approaches you could take for this:
If you're targeting iOS 5+ and iPad only, you can make a top-level view controller which has two contained view controllers. The first would be a view controller for your "TouchDisplay" view. The second would be the application's normal root view controller. (i.e. your existing main view controller; you'll need to set definesPresentationContext to YES on this view controller) Since you're writing the container view controller, you can order those two subviews however you like. There is a WWDC 2011 Talk on view controller containment that goes into great detail about this. This is the most "correct" approach IMHO, because it gives you a view controller for your TouchDisplay view, handles rotation and generally plays nice with others. (This only works on iPad, because on iPhone a new modal view always covers the full screen.)
A more straight-forward approach is to simply add your TouchView to your existing top-level UIWindow as a subview with addSubview:. Most applications don't actually remove the top-level view controller or add new top-level ones; they just present other view controllers from it. A view you add in the top-level window will stay above those. Of course, your app may not follow this rule, in which case you might try option #3 instead. This has rotation gotchas (your view will not auto-rotate when the device rotates, so you need to do this yourself.) You could also force your view back to top, say, on a 1-second timer, if you are having issues with other things covering it. This is also not as nice as option #1 because you don't get a UIViewController, just a UIView.
The most extreme approach is that you can create another UIWindow and give it a higher window level, such as UIWindowLevelAlert and put your TouchDisplay view in that. You can then make the window background transparent, and it will stay above your normal app content. There are lots of gotchas here, especially about auto-rotation and which window is the keyWindow (which is why you should use #1 or #2 instead if you can).
After some time I was able to get my app working. I have made an easy to use overlay that shows touch feedback over your existing application.
You can download the project here:
https://github.com/megaplow/FingerTracks/tree/master/FingerTracks
Happy coding,
Joe