I'm trying to implement a UISearchController with it's searchBar inside the navigationBar of a UINavigationController. This navigationBar has translucent and all Extend Edges turned off in Interface Builder.
The setup is quite simple:
ViewControllerWithSearchController is embedded in a UINavigationController. My viewController code looks like this:
class ViewControllerWithSearchController: UIViewController, UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate {
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
definesPresentationContext = true
initializeSearchController()
}
// MARK: - Search
func initializeSearchController() {
// Create and configure the UISearchController
searchController = UISearchController(searchResultsController: nil)
searchController.delegate = self
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.delegate = self
searchController.searchBar.sizeToFit()
searchController.hidesNavigationBarDuringPresentation = false
navigationItem.titleView = searchController.searchBar
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// Check the frame of the view
print(view.frame)
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
// Some code here
}
}
When I run this, it works. The frame of the ViewController's view on initial run is (0.0, 64.0, 375.0, 603.0)
This viewController contains a button which pushes a generic viewController onto the stack. When I pop this viewController, the frame of the ViewControllerWithSearchController is unchanged (correct behavior).
However, when I activate the searchBar and then push and pop the second UIViewController (while the searchBar is active), the frame of ViewControllerWithSearchController gets set to (0.0, 0.0, 375.0, 667.0). The view gets extended below the navigationbar, even though this behavior is turned off.
All involved ViewControllers have their Extend Edges options turned off in InterfaceBuilder.
For now, I've implemented a workaround by turning on all the Extend Edges options. This creates a bit of overhead, so I'd rather not do it. Is there a better solution to solve this problem?
I've attached a sample project to illustrate the problem.
Sample project
Related
I am using UISearchController in a Large header style. But when I push to the next view controller and coming back to the same controller again my UIView hides by navigation bar.
See Video
https://drive.google.com/open?id=1tb5Eeni-79uomBGl1GPEcESDSYOfrZ7v
I can't reproduce your error, but I'll post my test code, as it can help you.
I suggest that you write this code in a BaseViewController: UIViewController and extend it in your ViewControllers for define Large Titles:
self.navigationController?.navigationBar.prefersLargeTitles = true
Define your UISearchController and set your Instance.
var resultSearchController: UISearchController!
resultSearchController = UISearchController(searchResultsController: nil)
resultSearchController.searchResultsUpdater = self
tableView.tableHeaderView = resultSearchController.searchBar
Finally, you can try to close the Search keyboard when the screen disappears, like this:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
resultSearchController.searchBar.endEditing(true)
}
Hope this helps.
Adding following line in viewwillappear resolves my issue
extendedLayoutIncludesOpaqueBars = true
When I use storyboard segue, it is pretty smooth showing another viewcontroller onscreen. However, when I'm not using storyboard, just add a simple line of code with navigationController?.pushViewController(UIViewController(), animated: true), it's a little bit lagging in transition.
Also I read about Delay when pushing view controller (iOS). But even when I'm pushing an brand new viewcontroller (no extra code inside), the transition is still a little bit lagging, any idea?
Swift 5.3, Xcode 12
I was facing this same issue, tried adding to main thread, but apparently the problem was I was pushing the viewController created programatically, and it backgroundColor was nil.
Just by setting the color in pushedViewController to something else, solved the issue.
self.view.backgroundColor = yourColor
In app delegate, set your window's background color to red.
window?.backgroundColor = .red
Also in the the pushed view controller, set its view to red.
view.backgroundColor = .red
I experienced the same issue when programmatically embedding my view controller in a UINavigationController.
I came across the same issue when I was drawing UI programmatically.
In my case, overriding loadView function solved my problem.
import UIKit
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "My tasks"
}
override func loadView() {
// You can load your view here. For simplicity, I just give it a background color
let v = UIView()
v.backgroundColor = UIColor.orange
view = v // This line does the magic
}
}
Viewcontroller is expecting to render view so add below view background color or set title
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Title"
self.view.backgroundColor = .red
}
I debug with slow animation on simulator. The "lagging" is the underlying previous view controller’s view as #yesleon mentioned. I set the previous view's alpha to 0 when viewWillDisappear, and 1 when viewWillAppear. It seems much better when pushing to the new controller now, but when pushing back to original view controller, it still a little bit not perfect, any better solution?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = false
view.alpha = 1.0
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.tabBarController?.tabBar.isHidden = true
view.alpha = 0.0
}
#IBAction func barbuttonDidTapped(_ sender: UIBarButtonItem) {
navigationController?.pushViewController(UIViewController(), animated: true)
}
I met the same issue, put ui code and its constraints inside viewDidLoad into loadView() fix it. Now the animation is moving smoothly.
Refer here: https://stackoverflow.com/a/3424052/10158398
loadView is the method that actually sets up your view (sets up all
the outlets, including self.view).
viewDidLoad you can figure out by its name. It's a delegate method
called after the view has been loaded (all the outlets have been set)
that just notifies the controller that it can now start using the
outlets.
viewDidLoad: "This method is called after the view controller has
loaded its associated views into memory. This method is called
regardless of whether the views were stored in a nib file or created
programmatically in the loadView method."
loadView: "If you create your views manually, you must override this
method and use it to create your views."
For an example:
class DetailViewController: UIViewController {
let imageView = UIImageView()
var picture: Picture?
override func viewDidLoad() {
super.viewDidLoad()
}
override func loadView() {
view = UIView()
view.backgroundColor = .white
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
if let picture = picture {
let path = getDocumentDirectory().appendingPathComponent(picture.picture)
imageView.image = UIImage(contentsOfFile: path.path)
}
}
I have made a UITableView Controller with a UISearchBar as the Table's header.
I have then embedded this View Controller into a UINavigationController, as the root view controller.
Now, when I tap on the Search Bar, the SearchBar seems to disappears and displays a white screen. The keyboard appears, but there is no Search Bar.
The Table View can scroll, but the search bar has simply vanished.
When I implement this UITableViewController without the Navigation Controller, it works perfectly. But something about the Navigation Controller is borking everything up.
I've had the same issue that was sometimes happening, especially with table view of small number of rows (less than 50).
It appears the searchBar is removed from the view hierarchy, precisely from the container view that is a child of the UISearchControllerView.
I've found a workaround to manually add back the searchbar as a subview of the UISearchControllerView container child. This is implemented in the delegate function (from UISearchControllerDelegate) didPresentSearchController:
func didPresentSearchController(searchController: UISearchController) {
if searchController.searchBar.superview == nil {
for searchCtrlChildView in searchController.view.subviews {
if searchCtrlChildView.frame.origin == CGPoint(x: 0, y: 0) { //Discriminate if by chance there was more than one subview
searchCtrlChildView.addSubview(searchController.searchBar)
break
}
}
}
}
I've also filed a radar to Apple on this as it is not fixed in iOS 8.4
Check the way I had my searchBar it in viewDidLoad
I have my viewController embedded in NavigationController too
My code (hope it helps) :
class myTableViewController: UITableViewController,UISearchResultsUpdating,UISearchControllerDelegate,UISearchBarDelegate
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
I am relatively new to iOS (100 hours or so) and I am trying to implement a UISearchController without a storyboard. Is it possible to do this programmatically?
You have to implement UISearchController programmatically since Apple hasn't provided the ability to set it up using Storyboard yet.
Here are the steps to implement UISearchController in a view controller
Conform to UISearchResultsUpdating Protocol
Create a variable to reference the UISearchController
var searchController: UISearchController!
Set searchController in viewDidLoad(_:)
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
// The object responsible for updating the contents of the search results controller.
searchController.searchResultsUpdater = self
// Determines whether the underlying content is dimmed during a search.
// if we are presenting the display results in the same view, this should be false
searchController.dimsBackgroundDuringPresentation = false
// Make sure the that the search bar is visible within the navigation bar.
searchController.searchBar.sizeToFit()
// Include the search controller's search bar within the table's header view.
tableView.tableHeaderView = searchController.searchBar
definesPresentationContext = true
}
Implement the method updateSearchResultsForSearchController(_:), which is called when the search bar becomes the first responder or when the user makes changes to the text inside the search bar
func updateSearchResultsForSearchController(searchController: UISearchController) {
// No need to update anything if we're being dismissed.
if !searchController.active {
return
}
// you can access the text in the search bar as below
filterString = searchController.searchBar.text
// write some code to filter the data provided to your tableview
}
My UITableViewController is conforming to the new UISearchControllerDelegate and also UISearchResultsUpdating.
Here is my setup code for the search bar:
override func viewDidLoad() {
var searchController = UISearchController(searchResultsController: self)
searchController.searchResultsUpdater = self
self.tableView.tableHeaderView = searchController.searchBar
self.definesPresentationContext = true
}
However, when running this in the simulator there is no search bar in the table header, even though it is specified in the code. I also tried this code in viewWillAppear, but again no search bar was shown.
I was informed by an Apple Engineer that you must give the Search Bar a frame. If you print the frame of the search bar, you will notice it's height is zero. So this is probably a bug in Apple's code.
searchController.searchBar = CGRectMake(0.0, 0.0, 320.0, 44.0)
Edit:
The documentation specifies that you must pass in the View Controller that you want to display the results. To display this in the same View Controller you are in, pass in nil.
var searchController = UISearchController(searchResultsController: nil)