Making a uitextfield scroll to the bottom when adding an item - ios

I have a collectionViewController and an inputContainerAccesoryView that have a uitextfield inside. Every time I press return on the keyboard, an item (a row) is added to the collectionview, and I want the collectionview to automatically scroll to the bottom of it; also I want that when the keyboard shows up, it also scrolls to the bottom of the collectionview. I have a tried a code, but is not scrolling the way I want:
viewDidLoad
collectionView?.keyboardDismissMode = .interactive
NotificationCenter.default.addObserver(
self,
selector: #selector(HomeViewController.keyboardWillShow(event:)),
name: NSNotification.Name.UIKeyboardWillShow,
object: nil
)
textFieldShouldReturn
handleKeyboardDidShow()
The methods
func keyboardWillShow(event: Notification) {
//let keyboardqlq = inputTextField.frame.height
guard let keyboardFrame = (event.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
guard let colelctionview = self.collectionView else { return }
colelctionview.contentInset.bottom = keyboardFrame.height - 200
colelctionview.scrollIndicatorInsets.bottom = keyboardFrame.height - 200
// do NOT scroll messages if the keyboard is the `accessoryView`
if keyboardFrame.height != inputContainerView.frame.height {
handleKeyboardDidShow()
}
}
func handleKeyboardDidShow() {
if messages.count > 0 {
let indexPath = IndexPath(item: messages.count - 1, section: 0)
collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
}
}

Maybe it's not efficient but it solves the problem. Make sure that textfield delegate is self.
var needToScroll :Bool = true
func textFieldDidBeginEditing(_ textField: UITextField) {
needToScroll = true
}
func handleKeyboardWillHide(notification:Notification) {
let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
UIView.animate(withDuration: keyboardDuration!) {
self.view.layoutIfNeeded()
self.chatLogCollectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 30, right: 0)
self.needToScroll = false
}
}
func handleKeyboardWillShow(notification:Notification) {
let keyboardFrame = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? CGRect
let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
UIView.animate(withDuration: keyboardDuration!) {
self.view.layoutIfNeeded()
self.chatLogCollectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: (keyboardFrame?.height)!, right: 0)
if self.needToScroll {
if self.chatLogCollectionView.messages.count > 0 {
self.chatLogCollectionView.scrollToItem(at: NSIndexPath(item: self.chatLogCollectionView.messages.count - 1, section: 0) as IndexPath, at: .bottom, animated: false)
}
}
}
}

Related

Smooth animation in keyboardWillShow doesn't work with UITextView

Solution which worked for me
After struggling for days I finally manage to find the fix for view animation issue. For me keyboardWillChangeFrameNotification worked. The solution is discussed in the following thread:
Move textfield when keyboard appears swift
I've a bunch of views embedded inside stack view along with text view. I've given text view height of <= 120. In keyboardWillShow view doesn't animate despite adding code as required. I've played with duration value but it's all same result. I was wondering if it's due to text view? How to fix it?
#objc func keyboardWillShow(notification:NSNotification) {
guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
let keyboardScreenEndFrame = keyboardValue.cgRectValue
let keyboardFrame = view.convert(keyboardScreenEndFrame, from: view.window)
if #available(iOS 11.0, *) {
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardFrame.height - view.safeAreaInsets.bottom, right: 0)
} else {
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardFrame.height, right: 0)
}
scrollView.scrollIndicatorInsets = scrollView.contentInset
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height + keyboardFrame.height - scrollView.bounds.size.height)
scrollView.setContentOffset(bottomOffset, animated: true)
UIView.animate(withDuration: 0.5, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
===
extension FirstViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
let estimatedSize = textView.sizeThatFits(textView.frame.size)
if estimatedSize.height > textViewMaxHeight {
if estimatedSize.height - textViewMaxHeight < textView.font!.lineHeight && !didExpandTextView {
didExpandTextView = true
var contentInset:UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.savedKbHeight, right: 0.0)
if let v = self.tabBarController?.tabBar {
contentInset.bottom -= v.frame.height
}
scrollView.contentInset = contentInset
scrollView.scrollIndicatorInsets = contentInset
if textView.isFirstResponder {
let fr = textView.frame
scrollView.scrollRectToVisible(fr, animated: false)
}
}
textView.isScrollEnabled = true
textView.showsVerticalScrollIndicator = true
} else {
if let lineHeight = textView.font?.lineHeight, Int(estimatedSize.height / lineHeight) != numberOfLines {
numberOfLines = Int(estimatedSize.height / textView.font!.lineHeight)
var contentInset:UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.savedKbHeight, right: 0.0)
print("contentInset: \(contentInset)")
scrollView.contentInset = contentInset
scrollView.scrollIndicatorInsets = contentInset
if textView.isFirstResponder {
let fr = textView.frame
scrollView.scrollRectToVisible(fr, animated: false)
}
didExpandTextView = false
}
textView.isScrollEnabled = false
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
Try this approach:
func textViewDidChange(_ textView: UITextView) {
let estimatedSize = textView.sizeThatFits(textView.frame.size)
textView.isScrollEnabled = estimatedSize.height > textViewMaxHeight
if !textView.isScrollEnabled {
let maxBottom = self.view.frame.height - self.savedKbHeight
// Use following line if you have Extended Edges set
// let maxBottom = self.view.frame.height - self.savedKbHeight - topLayoutGuide.length - bottomLayoutGuide.length
var r = self.textView.frame
r.size.height = estimatedSize.height
let tvBottom = self.scrollView.convert(r, to: self.view).maxX
if tvBottom > maxBottom {
self.scrollView.scrollRectToVisible(r, animated: true)
}
}
}
Edit Note: This is not intended to be Production Ready code. There are many things that affect layout / positioning, and there is no guarantee that just "dropping this in" will work in all situations.

Manage Keyboard in Chat View

I have a controller like chat view, with one textfield and button and table view. I want to when user tap to textfield textfield go up and scroll to end of list. there is my code :
#objc func keyboardWillChangeFrame(_ notification: Notification) {
let keyboardFrame = ((notification as NSNotification).userInfo![UIKeyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue
print("*****************")
print((keyboardFrame?.height)!)
//emoji keyboard size is 258
if (keyboardFrame?.height)! == 258 {
textBackViewBottom.constant = 52
}
else {
textBackViewBottom.constant = 10
}
}
#objc func keyboardWillShow(_ notification: Notification) {
// Check for double invocation
if _keyboardShown {
return
}
_keyboardShown = true
// Reducing size of table
let baseView = self.view
let keyboardFrame = ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey]! as AnyObject).cgRectValue
print("////////////////////")
print((keyboardFrame?.height)!)
let keyboardDuration = ((notification as NSNotification).userInfo![UIKeyboardAnimationDurationUserInfoKey]! as AnyObject).doubleValue
let visibleRows = tableView.indexPathsForVisibleRows
var lastIndexPath : IndexPath? = nil
if (visibleRows != nil) && visibleRows!.count > 0 {
lastIndexPath = visibleRows![visibleRows!.count-1] as IndexPath
}
UIView.animate(withDuration: keyboardDuration!, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
baseView!.frame = CGRect(x: baseView!.frame.origin.x, y: baseView!.frame.origin.y, width: baseView!.frame.size.width, height: baseView!.frame.size.height - (keyboardFrame?.size.height)!)
}, completion: {
(finished: Bool) in
if lastIndexPath != nil {
// Scroll down the table so that the last
// visible row remains visible
self.tableView.scrollToRow(at: lastIndexPath!, at: UITableViewScrollPosition.bottom, animated: true)
}
})
}
#objc func keyboardWillHide(_ notification: Notification) {
// Check for double invocation
if !_keyboardShown {
return
}
_keyboardShown = false
// Expanding size of table
//
let baseView = self.view
let keyboardFrame = ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey]! as AnyObject).cgRectValue
let keyboardDuration = ((notification as NSNotification).userInfo![UIKeyboardAnimationDurationUserInfoKey]! as AnyObject).doubleValue
UIView.animate(withDuration: keyboardDuration!, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
baseView!.frame = CGRect(x: baseView!.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.size.width, height: self.view.frame.size.height + (keyboardFrame?.size.height)!)
}, completion: nil)
}
every thing work well with Iphone default keyboard, but when I'm using custom keyboard like Swiftkey my code is wrong and view broken.
How can get Update view when keyboard size and keyboard type change.
thanks for reply.

how can I make my tableHeaderView snap and unsnap to hidden/unhidden position when user scrolls up

I have a tableView.tableHeaderView which is a UITableViewHeaderFooterView.
I would like this header to initially not be displayed when the tableViewController is first presented. When the user scrolls down from the top of the tableView, I would like to present the header.
Then when the user slightly scrolls down, I want the header to snap to a hidden position. The behaviors is exactly like the archived chats header of the WhatsApp page where all your chat's are listed.
Is there any way to achieve this without a complex set of scrollview delegate calls?
I thought on previous versions of swift/xcode, the tableView.tableHeaderView kind of snapped up and down but I notice it's not doing that anymore.
I think the only solution might be overriding the scrollViewDidScroll. This is what I've done but when the tableView.headerView reappears, it does so over the first cell of the tableView. Not sure how to make it appear in the correct position.
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let myHeaderView = self.tableView.tableHeaderView as? MyTableViewHeaderFooterView {
let height = (self.navigationController?.navigationBar.frame.size.height)! + UIApplication.shared.statusBarFrame.size.height + self.searchController.searchBar.frame.size.height
if scrollView.contentOffset.y < -height {
UIView.animate(withDuration: 0.1) {
myHeaderView.frame.size.height = 44
}
}
}
}
This is what I've settled on which seems to work pretty well in all cases:
var minimumTableViewInset = CGFloat(88) // this can be adjusted to 32 when the tableView is in landscape. need observer for rotation to set that.
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "MyTableViewHeaderFooterView", bundle: nil), forHeaderFooterViewReuseIdentifier: "MyTableViewHeaderFooterView")
let listsHeader = tableView.dequeueReusableHeaderFooterView(withIdentifier: "MyTableViewHeaderFooterView") as! MyTableViewHeaderFooterView
listsHeader.alpha = 0
listsHeader.frame.size.height = 0
self.tableView.tableHeaderView = listsHeader
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let headerView = self.tableView.tableHeaderView as? MyTableViewHeaderFooterView {
let height = (self.navigationController?.navigationBar.frame.size.height)! + UIApplication.shared.statusBarFrame.size.height + self.searchController.searchBar.frame.size.height
if scrollView.contentOffset.y < -height {
if headerView.frame.size.height != 44 {
tableView.beginUpdates()
headerView.frame.size.height = 44
tableView.endUpdates()
UIView.animate(withDuration: 0.5) {
self.tableView.tableHeaderView?.alpha = 1
}
}
}
}
}
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if let headerView = self.tableView.tableHeaderView as? MyTableViewHeaderFooterView {
if scrollView.contentOffset.y > -(minimumTableViewInset - CGFloat(10)) {
if headerView.frame.size.height != 0 {
tableView.beginUpdates()
headerView.frame.size.height = 0
tableView.endUpdates()
UIView.animate(withDuration: 0.2) {
self.tableView.tableHeaderView?.alpha = 0
}
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
}
}
}
}
override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if let headerView = self.tableView.tableHeaderView as? MyTableViewHeaderFooterView {
if scrollView.contentOffset.y > -(minimumTableViewInset - CGFloat(10)) {
if headerView.frame.size.height != 0 {
tableView.beginUpdates()
headerView.frame.size.height = 0
tableView.endUpdates()
UIView.animate(withDuration: 0.2) {
self.tableView.tableHeaderView?.alpha = 0
}
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
}
}
}
}

Move CollectionView content above keyboard

I would like to move CollectionView Cells above the keyboard when keyboard its appear. I just got keyboard size then i changed UIEdgeInsets the bottom of Collection view but nothing happens
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(collectionV)
collectionV.dataSource = self
collectionV.delegate = self
collectionV.alwaysBounceVertical = true
collectionV.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 45, right: 0)
collectionV.backgroundColor = UIColor.white
collectionV.keyboardDismissMode = .interactive
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillShow), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillHide), name: .UIKeyboardWillHide, object: nil)
}
#objc func handleKeyboardWillShow(notification: Notification) {
print("will show?")
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
let userInfo = notification.userInfo!
let animationDuration: TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
ContainerViewBottomAnchor?.constant = -keyboardSize.height
collectionV.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: keyboardSize.height+8, right: 0)
UIView.animate(withDuration: animationDuration) {
self.view.layoutIfNeeded()
}
}
}
#objc func handleKeyboardWillHide(notification: Notification) {
print("will hide?")
let userInfo = notification.userInfo!
let animationDuration: TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
ContainerViewBottomAnchor?.constant = 0
self.collectionV.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 45, right: 0)
UIView.animate(withDuration: animationDuration) {
self.view.layoutIfNeeded()
}
}
On Simulator when i press (⌘k) its working(the Cells move above), but when i click on TextField to show keyboard isn't working (Cells not moving) and i still get (print("will show")), Also on real device not working.
That makes me confusing, Any help to figure out what's wrong here?
Edit Fix: let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
Thanks to #MichaelVorontsov

UITableView's contentInset.top is 20 points larger than it should be in iOS10

I have a SearchViewController, and as part of this ViewController I have a UISearchController (with a SearchResultsController called SearchResultsTableViewController). I notice that my SearchResultsTableViewController's UITableView contentInset.top is 20 points more than it should be, despite it being fine on iOS8/9 and having automaticallyAdjustScrollViewInsets = true. If I perform a segue from this ViewController to another, then go back, I see that the contentInset.top increases another 20 points. Again, this does not occur on iOS8/9. Here are a couple of images showing this behavior.
Here is the code for the two relevant ViewControllers.
class SearchViewController: UIViewController, UISearchBarDelegate, UISearchControllerDelegate {
#IBOutlet var tableView: UITableView!
#IBOutlet var backgroundImageView: UIImageView!
var searchController: UISearchController!
var backBarButtonItem: UIBarButtonItem!
var searchButtonTapped = false
#IBOutlet var titleLabel: SpringLabel!
#IBOutlet var loadingIndicatorView: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
let searchResultsVC = self.storyboard!.instantiateViewController(withIdentifier: "SearchResultsTableViewController") as! SearchResultsTableViewController
searchResultsVC.searchController = self
searchController = UISearchController(searchResultsController: searchResultsVC)
searchController.searchResultsUpdater = searchResultsVC
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = true
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchBar.placeholder = "Search Courses"
searchController.view.backgroundColor = UIColor.clear
searchController.searchBar.keyboardAppearance = .dark
self.setSearchBarCaretColor(UIColor(red: 0.24, green: 0.34, blue: 0.19, alpha: 1.0))
self.setSearchBarFontSize(17.0)
self.navigationItem.titleView = searchController.searchBar
self.definesPresentationContext = true
self.backBarButtonItem = self.navigationItem.leftBarButtonItem
self.backgroundImageView.clipsToBounds = true
self.titleLabel.alpha = 0
self.titleLabel.layer.shadowColor = UIColor.black.cgColor
self.titleLabel.layer.shadowOpacity = 0.8
self.titleLabel.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
self.titleLabel.layer.shouldRasterize = true
self.titleLabel.layer.rasterizationScale = UIScreen.main.scale
self.loadingIndicatorView.layer.zPosition = CGFloat.greatestFiniteMagnitude
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.perform(#selector(SearchViewController.showKeyboard), with: nil, afterDelay: 0.01)
searchBarTextDidBeginEditing(searchController.searchBar)
}
func showKeyboard() {
self.searchController.searchBar.becomeFirstResponder()
}
func hideKeyboard() {
self.searchController.searchBar.resignFirstResponder()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.titleLabel.delay = 0.1
self.titleLabel.animation = "zoomIn"
self.titleLabel.duration = 0.6
self.titleLabel.animate()
let selectedRowIndexPath = self.tableView.indexPathForSelectedRow
if ((selectedRowIndexPath) != nil) {
self.tableView.deselectRow(at: selectedRowIndexPath!, animated: true)
self.transitionCoordinator?.notifyWhenInteractionEnds({ context in
if (context.isCancelled) {
self.tableView.selectRow(at: selectedRowIndexPath, animated: false, scrollPosition: UITableViewScrollPosition.none)
}
})
}
}
func setSearchBarCaretColor(_ color : UIColor) {
let view = searchController.searchBar.subviews[0]
let subViewsArray = view.subviews
for subView in subViewsArray {
if subView.isKind(of: UITextField.self) {
subView.tintColor = color
}
}
}
func setSearchBarFontSize(_ pointSize : CGFloat) {
let view = searchController.searchBar.subviews[0]
let subViewsArray = view.subviews
for subView in subViewsArray {
if subView.isKind(of: UITextField.self) {
let textField = subView as! UITextField
textField.font = UIFont.systemFont(ofSize: pointSize)
}
}
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
//This doesn't animate for some reason, so don't use it.
if (UI_USER_INTERFACE_IDIOM() != .pad) { //Because the iPad (for some reason) doesn't ever show the Cancel button, so keep the back button.
UIView.animate(withDuration: 0.3, animations: { self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView()) }, completion: { Void in
self.navigationItem.setHidesBackButton(true, animated: false)
})
}
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
if (self.searchButtonTapped == false) {
self.navigationItem.leftBarButtonItem = self.backBarButtonItem
self.navigationItem.setHidesBackButton(false, animated: true)
} else {
self.searchButtonTapped = false
}
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
self.navigationItem.leftBarButtonItem = self.backBarButtonItem
self.navigationItem.setHidesBackButton(false, animated: true)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.searchButtonTapped = true
searchBar.resignFirstResponder()
}
}
Now here is SearchResultsTableViewController:
class SearchResultsTableViewController: UITableViewController, UISearchResultsUpdating {
var resultsArray = [CourseResult]()
var emptySearchResultsView : EmptySearchResultsView!
var keyboardActive = false
var keyboardHeight : CGFloat = 0.0
var searchController : SearchViewController!
var query = PFQuery(className: "Course")
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.backgroundColor = UIColor.clear
self.tableView.estimatedRowHeight = 140
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.keyboardDismissMode = .onDrag
self.emptySearchResultsView = EmptySearchResultsView.construct(self) as EmptySearchResultsView
emptySearchResultsView.translatesAutoresizingMaskIntoConstraints = true
self.emptySearchResultsView.isHidden = true
self.tableView.addSubview(emptySearchResultsView)
query.whereKey("institution", equalTo: "College")
query.limit = 20
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.registerKeyboardNotifications()
let selectedRowIndexPath = self.tableView.indexPathForSelectedRow
if ((selectedRowIndexPath) != nil) {
self.tableView.deselectRow(at: selectedRowIndexPath!, animated: true)
self.transitionCoordinator?.notifyWhenInteractionEnds({ context in
if (context.isCancelled) {
self.tableView.selectRow(at: selectedRowIndexPath, animated: false, scrollPosition: UITableViewScrollPosition.none)
}
})
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.unregisterKeyboardNotifications()
}
var lastSearch = ""
func updateSearchResults(for searchController: UISearchController) {
guard searchController.isActive else { return }
if (lastSearch == searchController.searchBar.text!) {
return
}
self.lastSearch = searchController.searchBar.text!
query.cancel()
self.emptySearchResultsView.isHidden = true
if (searchController.searchBar.text?.characters.count == 0) {
resultsArray.removeAll()
self.tableView.reloadData()
}
if ((searchController.searchBar.text?.characters.count)! > 0) {
self.searchController.loadingIndicatorView.startAnimating()
var searchString = searchController.searchBar.text!
searchString = searchString.lowercased()
var searchStringArray = searchString.components(separatedBy: " ")
searchStringArray = searchStringArray.filter { $0 != "" }
//print(searchStringArray.description)
query.whereKey("searchTerms", containsAllObjectsIn: searchStringArray)
query.findObjectsInBackground(block: { (results, error) -> Void in
if (error == nil) {
self.resultsArray = []
let courseResultsArray = results! as [PFObject]
for result in courseResultsArray {
//removed all of this jargon.
}
self.searchController.loadingIndicatorView.stopAnimating()
self.tableView.reloadData()
if (self.resultsArray.count == 0) {
self.emptySearchResultsView.isHidden = false
} else {
self.emptySearchResultsView.isHidden = true
}
} else {
print(error?.localizedDescription)
self.searchController.loadingIndicatorView.stopAnimating()
}
})
}
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if (self.keyboardActive == true) {
self.emptySearchResultsView.frame = CGRect(x: 0, y: 0, width: self.tableView.bounds.width, height: self.tableView.bounds.height - self.keyboardHeight - self.tableView.contentInset.top)
self.emptySearchResultsView.setNeedsLayout()
} else {
self.emptySearchResultsView.frame = CGRect(x: 0, y: 0, width: self.tableView.bounds.width, height: self.tableView.bounds.height - self.tableView.contentInset.top)
self.emptySearchResultsView.setNeedsLayout()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return resultsArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellContent: CourseResult = self.resultsArray[(indexPath as NSIndexPath).row]
let cell = tableView.dequeueReusableCell(withIdentifier: "CourseResultTableViewCell", for: indexPath) as! CourseResultTableViewCell
cell.backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
cell.backgroundColor = UIColor.clear
cell.courseLabel.text = cellContent.courseCode + " - " + cellContent.courseName
cell.universityLabel.text = cellContent.university
cell.facultyLabel.text = cellContent.faculty
cell.leftHandSideImageView.image = UIImage(named: cellContent.faculty)
cell.leftHandSideImageView.layer.shadowColor = UIColor.black.cgColor
cell.leftHandSideImageView.layer.shadowOpacity = 0.6
cell.leftHandSideImageView.layer.shouldRasterize = true
cell.leftHandSideImageView.layer.rasterizationScale = UIScreen.main.scale
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let schedulesVC = self.storyboard!.instantiateViewController(withIdentifier: "SchedulesViewController") as! SchedulesViewController
schedulesVC.selectedCourse = self.resultsArray[(indexPath as NSIndexPath).row]
self.searchController.show(schedulesVC, sender: tableView)
}
func registerKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(SearchResultsTableViewController.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(SearchResultsTableViewController.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func unregisterKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWillShow(_ notification: Notification) {
if (UI_USER_INTERFACE_IDIOM() != .phone) { //Because devices like iPads don't have they keyboards affect the error screen visibility much.
return
}
if let keyboardFrame = ((notification as NSNotification).userInfo?[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue {
UIView.animate(withDuration: 0.6, animations: { self.emptySearchResultsView.frame = CGRect(x: 0, y: 0, width: self.tableView.bounds.width, height: self.tableView.bounds.height - keyboardFrame.height - self.tableView.contentInset.top)
self.emptySearchResultsView.layoutIfNeeded()
})
self.keyboardActive = true
self.keyboardHeight = keyboardFrame.height
}
}
func keyboardWillHide(_ notification: Notification) {
if (UI_USER_INTERFACE_IDIOM() != .phone) {
return
}
UIView.animate(withDuration: 0.6, animations: { self.emptySearchResultsView.frame = CGRect(x: 0, y: 0, width: self.tableView.bounds.width, height: self.tableView.bounds.height - self.tableView.contentInset.top)
self.emptySearchResultsView.layoutIfNeeded()
})
self.keyboardActive = false
}
}
Each of these ViewControllers have their own representation in the storyboard.
Does anyone have an idea why I see this behavior, which is only seen in iOS10? The issue occurs on a wide range of devices from what I have tested.
Note: Just ignore any string parsing code related to the results or any other related code. I removed some of this stuff to try to make the code a little more coherent, along with json parsing code, etc
The only solution that has worked for me (which is not ideal) is to check if the device is running iOS10 and to dynamically adjust the contentInset.top based on the height of the status bar and navigationBar. Obviously I had to turn off automaticallyAdjustScrollViewInsets to do this.

Resources