UIPopoverController takes control and won't let go - objective-c

I have a few Popovers that I present from UIBarButtonItems.
The popovers are Property settings for an Object, Color, Size, Positioning, name, line thickness, etc.
As the settings in the popover change, the Object they are properties for also changes in realtime.
Most of the time a tap away from the popover clears it. Though sometimes something happens to the environment where the responder chain seems to be broken and a tap away from the popover no longer dismisses the popover. The Property controls in the popover still work, though there is no way to dismiss the popover.
On Popovers with TextFields, I can alter the text, dismiss the Keyboard and then I can Dismiss the popover normally.
Any Tips on where to start Logging and see who's on top of the responder chain and who's able to receive gestures/touches.
Thanks!

When I was debugging recognizers and such (similar), I put a UITapGestureRecognizer on the main background view, calling a method viewTapped:. In viewTapped: you can dismiss any outstanding popovers (handy if they are properties of the VC). You can also send resignFirstResponder if you worry about any textFields that might not be cooperating.
Are you setting any passthrough views? You might examine those. Normally you don't have any ability to tap outside the popover without dismissing unless you specifically set the passthroughViews.
Good luck,
Damien

Related

Is there a way to show a message (like UIAlertView) from a modal UIPopover?

I have an iPad app that uses a UIPopover from within a UIVIew; I need to show an alert-type message when a certain condition has been met.
The problem is using a UIAlertView from within the UIPopover, when the user taps on a button in the UIAlertView, it also dismisses the UIPopover, which defeats the purpose of the alert.
I tried using UIActionSheets, but they don't display at all, probably because they are not being called from a controller-type view.
Is there a way to circumvent this behavior?
No, and you shouldn't do that. Popovers are supposed to go away as soon as you touch anything else.
You could enlarge the popover slightly and make room for a status message. When the user creates an appointment that overlaps, you could display a message in the status area.
Or, you could dismiss the popover and display an alert with "ok"/"cancel" buttons. The OK button would create the overlapping appointment, and the cancel button would discard it.
You will need some place to save the info from the popover while you are waiting for the user to decide what to do with the alert. Perhaps have the popover pass a message back to the view controller it comes from, and then have the source view controller create the alert, set itself as delegate, and handle the responses from the user.
According to Apple's Human Interface Guidelines, it is OK to display a UIAlertView on top of a popover:
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/MobileHIG/Alerts.html
To quote specifically:
On iPad, don’t display a modal view on top of a popover. With the
possible exception of an alert, nothing should display on top of a
popover.
Displaying a UIAlertView from a popover does not automatically dismiss the popover. There is likely some of your own code being executed which is causing it to dismiss. For example, in a similar situation I had, I found that displaying a UIAlertView was invoking "shouldAutorotate" in my split view controller, and (due to earlier iOS bugs) I had placed code there to dismiss the popover. For iOS7+ this was no longer necessary, so I was able to move this code into willRotateToInterfaceOrientation, where it no longer causes dismissal of the popover upon display of UIAlertView, because in this case, even though "autoRotate" gets called "willRotateToInterfaceOrientation" does not.

Handle tap event by subview of UIScrollView while scrolling

I have custom UIScrollView subclass with some content views inside. In some of them I have UITapGestureRecogniser. All works fine when scroll view is not scrolling. But when it scrolling content views does not receive tap action. What is the simplest solution to handle tap action by subview while scroll view is scrolling?
Details:
MyScrollView scrolls horizontally. It contains a lot of content views (e.g. MyContentView). Each MyContentView has width about one third of MyScrollView width. So there are about 3-4 visible MyContentView elements at a moment. The main behavior of MyScrollView is to 1)make sure that after scrolling one of MyContentView elements will be at center of screen and 2)to scroll to center of MyContentView if user taps on it. So the main answer I hope to get is how to "properly" implement handling of tap action in MyContentView while MyScrollView is decelerating.
I found some same questions and answers but none of them satisfied me. The best was to implement gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: of UITapGestureRecogniser delegate. But in this case I sometimes (when I tap, make smaaaal drag and release finger so tap is steel recognizable(lets called it quasi tap)) have both tap and scroll events and it leads to bugs for me even if scroll view is not scrolling when I begin tap. When user make quasi tap my application tries to scroll to tapped MyContentView element and than immediately handle normal scrolling. It seems even more terrible, due to some other functionality start to perform after handling tap (it must not perform when normal scrolling).
I need solution where scroll view wait enough to decide it is not tap event and only then make scroll. Otherwise if tap event had recognized scroll must not happen.
You can go with the custom delegates methods as well, using #protocol. Implement those delegate methods in view controller where your UIScrollView has been added.
like in MyContentView:
In touchesBegan method,
[self.delegate contentViewTapped:self];
Now in ContainerView class where scroll view is added, implement that method:
- (void)contentViewTapped:(MyContentView *)myContentView {
NSLog (#"ContentView no: %d", myContentView.tag); // if tag has been set while adding this view to scrollview.
}
Go through the examples for #protocol.
Hope this is what you required.
Enjoy Coding :)
This is built into UIScrollView - take a look at the delaysContentTouches and canCancelContentTouches properties. This should alleviate the problem when dragging a small bit after a tap.
This is all system built-in behaviour. I would suggest sticking with what Apple has provided for the feel of your interface (how it reacts to small drags, for instance) so that your app doesn't feel out of place on a user's phone.
EDIT:
Alternatively, you could disable scrolling of your scroll view in you gesture recognizer and re-enable it once it's ended/cancelled.
Further Edit:
I don't understand - I've created a sample project that illustrates how to intercept touches in a subview of a scroll view using gesture recognizer delegate methods. Play close attention to the "Cancellable Content Touches" and "Delays Content Touches" properties of the scroll view. They're both YES for very important reasons.
You scroll view should be delaying content touches until it has determined if the user is attempting a tap, pseudo-tap (as you put it), or a pan for the scroll view. Apple has already written the functionality you're trying to build; UIScrollView will already do what you want.
The problem is that the system doesn't want a scroll view's subviews intercepting tap events while the scroll view is scrolling. To this end, it cancels touch events if it determines that the user is actually trying to pan. Setting "Delays Content Touches" enables this behaviour. Ensure it's turned on and you should be fine.

How to suppress virtual Keyboard slide-in animation?

I've got a problem with creating a modal search view that emulates the behaviour of that of the Weather app. Specifically, there are two animations, that are bothering me and introduce unneeded 0.2 s delays:
When the modal view becomes visible, I give focus to the UISearchDisplayController.searchBar by caling becomeFirstResponder in viewDidAppear. However, the keyboard is not visible, when the modal view has slid in, but needs another 0.2s to slide in after the animation of tehe modal view transition is complete. Moving the call to another callback like viewWillAppear or viewDidLoad did no good, the keyboard won't show up in the first place.
When the user touches cancel, there is another animation taking place, before the delegate's searchDisplayControllerDidEndSearch method is called, expanding the search text field and "melting" away the button. Again, this animation is unneded as the modal view is supposed to transition out when the button is touched.
Additionally, when I dismiss and re-present the same view, not only does the keyboard slide in after the transition, but the cancel button does the same (luckily simultaneously).
I am aware of a similar problem described here: Keyboard Animation Issues When Calling becomeFirstResponder within a Modal View Controller.
However, it seems like the behaviour of the search bar is sligtly differet then that of text field. I could not reproduce the steps described by that author to make the keyboard visible by calling becomeFirstResponder in viewDidLoad.
Regards,
Chris
I think I found your answer. When you add a search bar using the interface builder, you can do it two ways: "Search bar" and "Search bar and Search Display Controller".
I was using the second and was having the very same problem you described. I could only invoke the keyboard (using becomeFirstResponder) on "viewDidAppear". But if you do it adding just the search bar it works. Now I can call becomeFirstResponder on "viewDidLoad" and the keyboard appears together with the view itself.
I means a little more work, but really not much. You have to set your controller to be the delegate of the search bar. I added a list view for the results and made my controller become its delegate and its datasource.

UISearchDisplayController not displaying keyboard when text area touched

I have a UITableView in a controller that is nested under a UITabBar.
The interaction is all wired up in Interface Builder so far, nothing done programmatically in terms of view switching.
I've added a UISearchDisplayController as the header of my UITableView. It displays fine, and when I tap on the text entry area, the cancel button appears and the black overlay flies in.
However, the keyboard never appears and when tapping the cancel button, the overlay flies out and the cancel button disappears, but the text entry area keeps focus and the caret stays flashing there, so I cannot tap there again to re-display the search results.
So essentially I have two problems:
Keyboard not appearing when starting to edit text on UISearchBar from UISearchDisplayController
UISearchBar not loosing focus when cancel button is tapped.
What am I doing wrong?
The .xib file that had my tab bar in it contained a UIWindow.
This lead to all sorts of craziness and in the end I gave up on trying to do this with interface builder, and resorted to constructing the UITabBar in code, thereby not creating a second UIWindow.
This resolved the problems and the UISearchDisplayController behaved correctly.
check this method in UISearchBarDelegate:
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar;
Try to see if this is getting called and do keyboard-related removal in here. If not, try making another UISearchDisplayController. (I actually never use the default viewController's one). Also, make sure the delegate is correctly set.

UIButton firing selector inconsistantly

I have a UIButton linked up in IB correctly(I believe). The button fires inconsistently, every time I reload the view to show updated info, the button works sometimes and sometimes does not.It gives no errors. I can't find a pattern to when it works and when it doesn't, the same code is run every time I open the view and it still works when it wants. Besides linking it in IB I have also tried to addTarget in ViewDidLoad and remove the IB connection but it still has the same inconsistency,
[_buttonScreen addTarget:self action:#selector(buttonScreenClicked) forControlEvents:UIControlEventTouchUpInside];
If I add NSLog(#"Clicked"); to buttonScreenClicked I see that the method doesn't always get called, what would cause it to do this, I have made sure that I set:
[_buttonScreen setAlpha:0.1];
[_buttonScreen setHidden:NO];
[_buttonScreen setUserInteractionEnabled:YES];
I have no Image, text, or color in the button, but it still works sometimes.
I'm using AFKPageFlipper on the same view but it still had the same problem before I added AFKPageFlipper, so I don't think its that.
If anyone could point me in any direction to start trouble shooting this problem I would appreciate.
Thanks
I just had the same problem and worked it out. The 5 seconds is the clue.
Somewhere you have a gesture recognizer covering the same space as your button. More specifically you have a gesture recognizer that is eating your Taps but not your LongPresses. If you just tap the button the gesture recognizer runs off with your event; Hold your finger down long enough and the gesture recognizer no longer considers it a tap and the event is passed through to your button.
Instrument your Tap gesture recognizer handlers and the problem should pop out at you.
Make sure you don't have any other UIView descendants overlaying the button (like a transparent UIScrollView) as these will intercept the touch events first.
Also make sure that the containing view (the view with the button in) is correctly sized, by default you can place a view outside the bounds of another view and the clipsToBounds is set to false so you will see it but not be able to interact with it.
Things to try:
Do you have any other actions on the button?
Do you have any other UIViews which could possibly be accepting the key presses (above or below, or un-shown)
Also, please check that you have only one UIViewController instance for this screen. Other issues may arrise because of that.
What happens if you dont set the alpha level?
Do you release the object properly in the dealloc only ?
Hope this helps