How to retain UISearchBar FirstResponder status on UICollectionView reload? - ios

A custom UIViewController contains a UICollectionView. A UISearchBar should be placed at the very top of the collectionView and scroll with its content. If the user enters a search text into the content fo the collectionView should be updated while the searchBar retains its FirstResponder status.
Something pretty easy which seems to be astonishingly to realize.
I found several other topics dealing with kind of problem but they are all several years old and none of the proposed solution worked. I wonder what is the best solution in 2020 using Xcode 11 and iOS 13 to solve this problem.
What DID NOT work:
I have added the UISearchBar to the header of the first section. When reloading the complete contentView or its sections the searchBar looses firstResponder and the keyboard is dismissed.
I have added the UISearchBar as subview to the collectionView and used the collectionViews contentOffset to place the content below the search bar. Although the searchBar is part of the collectionVies dynamic view hierarch, the Problem is the same es before: When reloading the complete contentView or its sections the searchBar looses firstResponder and the keyboard is dismissed.
I have added an extras section to the collectionView which only shows its header (including the searchBar) bot not content. When reloading the other sections but keeping this "search section" the keyboard is dismissed.
It makes no difference if collectionView.performBatchUpdates is used or not.
Resetting the firstResponder Status after the collectionView or its sections does work. However in this case the keyboard is dismissed and re-appears immediately. Quick typing is not possible this way and the flashing of the keyboard is very annoying.
Is there any known clean approach to solve this?

Meanwhile I found a solution that works for me. Maybe it is also helpful for others:
I have added the searchbar as additional subview to the collectionView and used the contentInsets to move the actual content below it. Problem with this solution was, that when calling collectionView.reloadData() the searchbar lost its firstResponder status and the keyboard disappeared.
So, before calling collectionView.reloadData() I simply moved the searchbar to another view. Since it is not longer a subview of the collectionView it is not effected by the reload and does not loose firstResponder:
collectionView.superview.addSubview(searchBar)
collectionView.reloadData()
collectionView.addSubview(searchBar)
Not the prettiest solution but it works.

Related

UISearchbar inside UITableView as the last cell hiding the keyboard

I have a UITableView in which I have several custom cells and my last cell contains a UISearchbar in it. I have wired up the appropriate delegates and referencing outlets and can get the keyboard to show up.
The problem I am facing is that the searchbar is at the very bottom of my tableview i.e. it is part of the very last cell. So even though the keyboard gets shown, the searchbar is hidden below it as I suspect it is unable to scroll to that location since its the last cell in the view.
Does anyone have any thoughts on this and how to overcome this situation.
The easiest solution is to have your view controller be a subclass of UITableViewController. This type of view controller will handle issues exactly like these.
If you cannot, you should listen to keyboard will show/hide notifications and set contentInset and scrollviewIndicatorInsets bottom of the table view to the keyboard height. Then you can scroll the specific cell into visible. When the keyboard hides, set the bottom to the previous value.
Yes....Two ways
1) either you can change the frame of whole tableView and pull the whole table up by decreasing the y position of tableView
[tableView setFrame:CGRectMake(100,100,100,100)];
or
2) You can change the contentOffset of the table to programatically scroll the tableView's last cell so that it is visible to the user.
[tableView setContentOffset:CGPointMake(0,400) animated:YES]
Hope this will help you.

UITableViewController weird behavior after popping a view controller

My UITableView has a bunch of reusable cells, and when I tap on one of them, it takes me to another view controller (via push segue) showing the details of that cell (let's say it's an item, so it would show details about an item - name, price, image, etc...). When I pop that view controller (by tapping on the back button), the UITableView has a strange behavior:
a) if it's scrolled all the way to the bottom, it will scroll automatically tad up (around 50 points), leaving the last cell barely visible, so I have to scroll back down again. My cell all have 60 points for height.
b) the scrollbar always shows and then disappears, indicating that something is moving that UITableView (although if not scrolled to the bottom, the content will not move automatically).
This happens in multiple UITableView's I have in my app. I am not forcing a reload of the table view in viewWillAppear, so I don't understand what is happening. My content is static after loading from the server (unless the user changes it, and then the reload is executed). But simply showing details of an item and popping that VC doesn't change anything in the table view.
Edit: Okay, I've figured what the problem is: I'm hiding a UIToolbar when pushing that segue. If I keep it always visible (which I don't want), it still shows the scrollbars animating when popping in my table view but doesn't scroll the table view if on the last few rows.
Add the following to viewDidLoad.
self.automaticallyAdjustsScrollViewInsets = NO;
This solved my problem of table view moving down after navigating back to view controller.
I managed to fix the first issue. It seems like the tableview is not taking into account the 44 points of the UIToolbar.
Save the tableview offset in prepareForSegue: (save it in a CGPoint property)
self.tableViewScrollOffset = self.tableView.contentOffset;
Then, in viewWillAppear:, check if it has been modified. If so, restore it.
if(self.tableView.contentOffset.y != self.tableViewScrollOffset.y) {
[self.tableView setContentOffset:self.tableViewScrollOffset];
self.tableViewScrollOffset = CGPointZero;
}
This behavior is indeed a bug in iOS 8.x.
All answers given so far can not really solve the issue. The issue is, that iOS forgets (or doesn't) consider the previously calculated cell sizes, when a table is being redrawn for instance when the view is being pushed.
One approach to solve this can be found here: UITableView layout messing up on push segue and return. (iOS 8, Xcode beta 5, Swift) (so this question is even a duplicate to this one).
However, the solution provided there is overkill and there are certain situations why this caching will fail (for instance a UIContentSizeCategoryDidChangeNotification is not regarded)
But there is a quite simpler solution even though it is odd:
If you are using a manual performSequeWithIdentifier in didSelectRowAtIndexPath, just add a [self.tableView reloadData] just before.
If you are using a IB seque from the cell, just add [self.tableView reloadData] in your prepareForSeque code.
The reason, why this solves the issue is, that this will force iOS to re-estimate the visible cells and so it no longer scrolls the content to another location. Fortunately, tableView reloadData doesn't cost too much overhead here as only the visible cells will be re-estimated.
Just a hunch, have you got a rogue scrollToRowAtIndexPath:atScrollPosition:animated hanging around?
I was also facing this issue. I managed to find it out. The reason in my case is tableview header height was calculating based text and text height was negative due to which tableview was shifting down even though the contentinset and scrollinset are zero.
This was only occurring for first time. Next time it is calculating correct. One weired thing i found is that when Class A (having tableview) have pushed another Class B from init. When keyboard from Class B is opened viewDidLoad of Class A is called. and before Class B is unloaded from navigation controller. Tableview is reloaded for Class A.
Setting the automaticallyAdjustsScrollViewInsets as suggested above did not work neither did caching and setting the tableViewScrollOffset work.
Hence came up with an workaround which worked like a charm for me.
The workaround was to add an Dummy UIView which has height of 1px and width of 320px and place it between the "Top Layout Guide" and the UITableView. This view's background could be set to clear so that it is invisible.
Now using Autolayouts, fix the Dummy View's top to the Top. Now set the tableview's top constraint with respect to Dummy View. Found that this resolved the issue of the tableview's misplacement.
Screenshot of the Dummy View along with the autolayout constraints have been provided for easy reference. The Dummy View has been set to a larger height and red background colour for illustration purpose only.

UITableView will not resize after UITextField becomes first responder

I have two Scenes in my Storyboard that are nearly identical. Both are UITableViewControllers. Both have header and footer views. The header views have a UISearchBar and the footer views have a UIView that contains a UITextField. Each have only one prototype cell. One is prototyped as a "Basic" cell and the other is prototyped as "Right Detail" cell.
Here's the problem. When I click the UITextField in the footer view on the first scene, the table resizes automatically so that the bottom of the table is at the top of the keyboard. This allows me to scroll the table up so the footer view shows and the user is able to see what they're typing. The other scene will not automatically resize the UITableView so the UITableView cannot scroll the footer view to where it can be seen and the UITextField is hidden under the keyboard. I can't even manually scroll the table far enough since the footer is always at the bottom of the UITableView.
A little added info. The scene that works has many rows of data while the one that doesn't work only has a couple. I tried adding a number of rows until the table had enough to enable scrolling and it doesn't fix the problem.
I have checked everything I can think of and I can't see anything that would allow one of the views to let the UITableView to automatically resize to work with the keyboard and the other not. I must have overlooked something but I can't seem to find it.
Any help will be greatly appreciated!
Rob
I thought this problem was caused by copy-and-pasting from one view to another, but I had the same problem once I'd (in theory) fixed it.
The answer for me turned out to be simple: I hadn't called [super viewWillAppear:animated] in my UITableViewController subclass' viewWillAppear: implementation. Make sure you've got a call to the superclass' method and hopefully the problem will go away.
I am guessing the frame of the tableview is not being resized to the smaller size in the second case.
Print out the frame and content sizes in both the cases once its loaded, that should help you see if there is an issue.

iOS: Disable UITableView animation when keyboard shows up

Everyone wants to move the UITableView when the keyboard pops up, but I'm looking for a way to disable the automatic animation to the cursor when the keyboard pops up. I'm experiencing an odd jerking / jolting / erratic scrolling behavior when the keyboard pops up and causes the UITableView to scroll to the cursor (to avoid blocking it).
Each of my UITableView cells has a UITextView in it. I don't commit any other animations when the keyboard pops up.
At this point, I would like to disable the animation completely and manually scroll to a desired CGPoint.
Thank you!
The automatic scrolling code resides in tableViewController, so auto-scrolling can't be disabled. Instead of subclassing from UITableViewController you can subclass from UIViewController and use a tableView inside it.
If you are willing to use UITableViewController itself, you can override viewWillAppear and don't call [super viewWillAppear].

UITableView won't scroll with a UISearchBar

This should be a fairly general question, but I have a view in which I placed a UITableView and implemented it properly. It works well and it scrolls. But I want to add a UISearchBar attached to it so that it will scroll along with the table. But when I add the search bar onto the tableview it shows up, but the tableview will not scroll anymore. I don't actually need any input into the search bar, and I disabled user interaction. Is there any way to add the search bar (no need to implement it) and have it scroll?
(I will rate up anyone who answers!)
Thanks
The UISearchBar should be the UITableView's header view.
Had similar issue even when IB had the correct bouncing properties enabled.
In your viewDidLoad, set self.tableView.alwaysBounceVertical = YES;.

Resources