UISearchDisplayController's full-screen background intercepts touch events in iOS 7 - ios

I have a UITableview that doesn't take up the whole screen (screenshot). Everything worked fine in iOS 6. But in iOS 7, when the user searches, the search result table takes up the whole view (screenshot).
To fix this, I tried setting the frame manually as described in this answer. The appearance is now correct (screenshot), but now the "<" button in the top left doesn't receive tap events when the search results table is displayed.
It seems the searchResultsTableView is adding a full-screen background view that is intercepting touch events. To prove this, I added this code to didShowSearchResultsTableView:
controller.searchResultsTableView.superview.backgroundColor = [UIColor blueColor];`
This screenshot confirms my hypothesis.
How can I fix this to allow the "<" button to receive tap events?
I want to avoid modifying controller.searchResultsTableView.superview so that my change doesn't break in future versions of iOS.
And what change in iOS 7 caused this behavior to start happening?

I am still searching for a better solution, but currently my solution is in the viewControllers viewDidLayoutSubviews tell your view to move to front. The code would look something like this.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self.view bringSubviewToFront:self.navigationBar];
}

Related

UITableView: Can't find a way to move/align the tableIndex element up

I have a UITableView and I am using a tableIndex, my problem is that when the keyboard comes up, it partially hides the tableIndex.
An acceptable solution would be to move the tableIndex up (like what the Contacts App does) when the keyboard appears, and show the most of it, but can figure out a way to do this. Any ideas of code snippet I can try?
The way to do this is to register to Keyboard Will Show notification and then update the height of your controller's view. You could also embed everything in a scroll View so that once the keyboard rises, your view becomes smaller and scrollable.
It is honestly a bit annoying to set up, so, if you are open to using CocoaPods then IQKeyboardManager is probably your best bet which can be found here: https://github.com/hackiftekhar/IQKeyboardManager

My UIScrollView (containing some UIControls) doesn't scroll anymore in iOS 8

In my app I have made a calendar which basically consists in a UIScrollView containing some cells (custom UIControl) for each day, a top header containing some labels (Mon, Tue, Wed...), and obviously some separators (UIView of 1px thickness).
Everything worked fine on iOS 7, I was able to select the cells by pressing them (I use the UIControlEventTouchUpInside event to select them), and to drag the paged scrollview to display the previous or next month.
But since I've switched to iOS 8, and downloaded Xcode 6, it doesn't work anymore.
In fact I can still select the cells, but the scrollview doesn't scroll (except if I drag it from the header or a separator, which are basic UIView without any touch event management).
Il I start dragging the scrollview from a cell, it just stay fixed.
I didn't change anything, so I don't understand why it doesn't work anymore...
Please help me!! :)
Thanks a lot
PS: I'm french, sorry for my bad english
EDIT: with iOS 8.1 the behavior seems to be similar to behavior before iOS 8.0, so Apple might have 'fixed' it. So the answer below is not needed any more.
There has been a change in iOS 8, as far as I can see, which influences the handling of UIScrollview. UIScrollView does not track content touches any more in case of an UIControl.
One thing I did, is to subclass UIScrollView and overwrite touchesShouldCancelInContentView
- (BOOL) touchesShouldCancelInContentView:(UIView *)view {
return YES;
}
See also https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIScrollView_Class/index.html
Return Value: YES to cancel further touch messages to view, NO to have
view continue to receive those messages. The default returned value is
YES if view is not a UIControl object; otherwise, it returns NO.

UITextField keyboard dismissal behaviour different in iOS 6

I have some simple code that moves the 'firstResponder' between UITextFields by calling 'becomeFirstResponder' on each field in turn.
In iOS 5, the keyboard is shown as constantly, until the final textfield where resignFirstResponder is shown.
However, in iOS 6, the keyboard performs the dismiss and reappear animation each time the first responder is moved. At no point do I call resignFirstResponder when calling becomeFirstResponder to move the responder (or in any of the associated methods) - it just seems to be new default behaviour in iOS 6.
This is distracting, and unwanted.
I've looked through the Apple documentation, and can't find any mention of a change to how the keyboard animation is managed.
Can anyone refer me to documentation of these changes? Thanks.
Found the problem.
My code was also calling,
[self.tableView reloadData];
...when calling becomeFirstResponder.
Therefore, the keyboard was being dismissed while the redraw occurred.
This certainly seems to cause different keyboard behaviour between iOS 5 & 6. Though the behaviour in 6 doesn't seem wholly unreasonable, considering.
Thanks to #Fogmeister for the help.

iOS: Tapping on UITableView does not call didSelectRowAtIndexPath

My app uses a UITableView with a UINavigationController to show a more detailed view when a row of the table is tapped - the basic drill-down routine.
When I tap on a row, it is highlighted, but the delegate methods tableView:willSelectRowAtIndexPath: or tableView:didSelectRowAtIndexPath: are not called (verified using debugger).
Now here's the weird part:
There are some other table views in the app (they don't drill down) and none of them exhibit the issue.
If I tap the row rapidly and repeatedly, after many tries (10 - 20 is normal), tableView:willSelectRowAtIndexPath: and tableView:didSelectRowAtIndexPath: are called and processing continues normally.
The problem occurs only on an (any, actually) iPad running iOS 6. It works fine with iPads running iOS 5, or with any iPhone running any iOS version, 6. It also works with the iPad simulator using iOS 5 or 6.
So it seems that something is receiving the tap before the delegate methods are called. But what?
I not using any UITapGestureRecognizer, so that is not the issue.
I am not using multiple UITableViewControllers for the table, so this is also not the issue.
I was having this issue with an app when running in iOS6. The tableView:didSelectRowAtIndexPath: method was never being triggered, even though it was working before updating to iOS6.
I finally figured it out by looking at the xib and the attribute inspector for the table. In the Table View section I had the Selection option set to No Selection and Show Selection on Touch was not selected.
Changing the Selection option to Single Selection actually got this working for me again and leaving the Show Selection on Touch unselected means that I don't see any flash or change of colour when the row is selected.
I encountered a similar issue. iOS 6 appears to be much more sensitive to the slightest upward movement on a tap on a table view cell. It appears it is registering the slightest movement as a scroll request rather than a cell selection. My table view was set to scrollEnabled = NO because it is a table view with static selection values. The table view appears within a small area of a larger iPad view. If I carefully tap a cell and lift directly up, the cell is selected.
To resolve it, I changed scrollEnabled to YES and made sure the area dedicated to my tableView was larger than the actual area needed to show the table view, thus preventing it from scrolling. Not sure if this is is the cause of the issue you are experiencing, but hope it helps.
I had the same problem. The UITableView was not triggering the didSelectRowAtIndexPath method when running in iOS6, but it was working fine in iOS5.1.
I had also implemented the
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath{
return NO;
}
and returned NO. In iOS5 it was all working fine, but the minute I switched to iOS6 it would cease to trigger the didSelectRowAtIndexPath. Once I removed that shouldHighlightRow method, everything worked in iOS6 again. Cheers!
The usual reason that didSeletRowAtIndexPath() doesn't get called is having a UITapGestureRecognizer on your viewcontroller with Cancels touches in view set to YES.
The correct and only sensible fix is to set Cancels touches in view to NO. Then your table row selection should work nicely.
Obviously this has some implications in your gesture handlers - you may need to add some code to your gesture handler to stop some touches going on to your views i.e.
- (IBAction)onTapGesture:(UITapGestureRecognizer *)sender {
if (sender.view == someOldViewThatINeedToDealWith) {
sender.cancelsTouchesInView = true;
}
}
I tried all the other answers posted, and although they seemed promising, they didn't solve the problem.
I've since discovered the cause of problem in my case: I was calling [self.tableView reloadData] on a different thread (due to handling an NSNotification event posted from a background worker thread).
I fixed the problem by changing [self.tableView reloadData] to
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
Hope this helps.
The other suspect could be a Tap Gesture Recognizer set to the view controller's view swallowing all taps like a blackhole. Remove the gesture recognizer will enable did select. I just had the same issue and the tap gesture was the cause.
The resolution is really weird: the UITableViewController registered for all notifications. In the handler for the notifications, the table view data was being reloaded using
[self.tableView reloadData]
According to Apple DTS, when the table reloads data, this causes a notification to be sent to the observer, causing a race condition ...
No explanation for why it would work sometimes or why it would always work on an iPhone. But registering only for a small subset of notifications (i.e. the ones I was really interested in) fixed the problem!
Setting
recognizer.cancelsTouchesInView = NO;
takes care of it if your use case allows simultaneous handling
of touches by [tap] gesture recognizer (kdb dismiss for example)
and the view the recognizer is attached to.
in my case I hade button which received all touch events before tableviewcell

Why UISearchBar and its scope buttons are shown in one line?

I have seen Apple's example "TableSearch" that when touched its scope buttons come below the search bar.
http://developer.apple.com/iphone/library/samplecode/TableSearch/Introduction/Intro.html
But when I make my own it looks good at first but when I touch it looks like ugly, scope buttons and search bar are shown in the same line like this:
http://cl.ly/BN9
What do I have to do make it like "TableSearch" example in the iPad?
I am doing everything in IB and tried to modify the search bar programatically from the controller:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.rowHeight = 88.0f;
self.tableView.contentOffset = CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height);
self.searchDisplayController.searchResultsTableView.rowHeight = self.tableView.rowHeight;
//BELOW DID NOT WORK:
CGRect b = self.searchDisplayController.searchBar.bounds;
self.searchDisplayController.searchBar.bounds = CGRectMake(b.origin.x, b.origin.y, b.size.width, self.tableView.rowHeight);
b = self.searchDisplayController.searchBar.frame;
self.searchDisplayController.searchBar.frame = CGRectMake(b.origin.x, b.origin.y, b.size.width, self.tableView.rowHeight);
//BELOW WORKS PERFECT BUT IS A PRIVATE METHOD, HENCE I AM NOT SUPPOSED TO USE IT
//[self.searchDisplayController.searchBar setCombinesLandscapeBars:NO];
}
Thanks in advance.
I've encountered this bug as well, and I've both filed a report with Apple and requested technical assistance. I'll let you know how it goes. In the meantime I'll give you a brief bit of background on this bug.
On the iPhone, to preserve precious vertical screen real estate in Landscape mode, the UISearchDisplayController sets the UISearchBar to combine its search bar and search field in a single horizontal layout. This works pretty well because of the increased horizontal size of the screen (480 points in Landscape). Unfortunately, it works not so well on the iPad, where in Landscape mode the UI change really isn't necessary in the first place because you have plenty of vertical real estate. You also still only have 320 pixels of horizontal display space in the master view of the UISplitViewController, not the increased 480 of the iPhone. The result is an iSore.
Presumably the problem is that UISearchDisplayController is behaving badly in its willRotateToInterfaceOrientation:duration‎: method. Specifically, it's not bothering to check whether it's on an iPhone or not before it sets the combinesLandscapeBars property on its UISearchBar. The private API kludge in your code works because it fixes that oversight in the UISearchDisplayController. But of course Apple will rain down the fury of the ancients on you for using undocumented APIs, so you can't. Really we're at the mercy of Apple on this one.
If you're willing to give up the eye-candy and convenience of UISearchDisplayController, you can use a UISearchBar sans UISearchDisplayController and manage aspects of presentation yourself. Obviously this requires a lot more code and would be pointless if Apple's API engineers did their jobs, but it will at least resolve the display bug.
If you're Apple you can use your own undocumented APIs, which is why Mail.app doesn't have this problem.
UPDATE
The bug report I've filed with Apple is #8344719.
Using the following code, you will not get warning:
if ([self.searchDisplayController.searchBar respondsToSelector:#selector(setCombinesLandscapeBars:)])
{
objc_msgSend(self.searchDisplayController.searchBar, #selector(setCombinesLandscapeBars:), NO );
}
When you set the bounds and frame for the searchBar, like for the frame here:
blabla.searchBar.frame = CGRectMake(b.origin.x,
b.origin.y,
b.size.width,
self.tableView.rowHeight);
it seems like there is a problem with height. Scope buttons require some space under the search bar, so you should increase the height of both bounds and the frame.
If you show and hide scope buttons on some event you need to adjust the frame size each time.
I'm getting this issue on an iPad, but I can get it to work if I insert this in my implementation file:
#synthesize tableView;
I'm guessing there is something bad with the XIB being loaded, but I don't know why this would fix it.

Resources