Ran through a quick tutorial with search bars and figured that I could use searchBar.sizeToFit() to autosize the search bar, however the right end of the search bar still extends off of the screen.
class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating, UISearchBarDelegate {
var searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
definesPresentationContext = true
}}
I've tried to manually set the frame with something like searchController.searchBar.frame = CGRectMake(0, 0, 200, 200) but the width remained off of the screen. The search bar does not exist in the storyboard, however the tableView does.
Needed to set autolayout constraints for the tableView that the search bar was a part of. Setting these fixed the sizing issues.
If you're using Auto layout, then use leading and trailing edges instead of width constraints.
Just click on Pin option given below and select top,leading(left) and trailing(right) constraints, then click on Add 3 constraints. Make sure "Constraint to margin" checkbox is unchecked. If the constraints satisfy, There will be no warning. Try changing the background color of your search bar to see its position on the screen.
If setting up auto layout constraints doesn't fix the problem, try calling searchController.searchBar.sizeToFit() in viewWillLayoutSubviews() rather than viewDidLoad(). This may help if the search bar is contained in a view other than the table header view.
Swift 4, works for all types of ViewControllers
After countless hours of trial and error here's what I came up for those who want to use it in code:
Set delegate
class SearchDatasourceController: UIViewController , UISearchBarDelegate { }
Set a customTitleView and the searchBar
IMPORTANT: set their frame
lazy var customTitleView: UIView = {
var view = UIView()
let frame = CGRect(x: 0, y: 0, width: 300, height: 44)
view.frame = frame
return view
}()
lazy var searchBar: UISearchBar = {
let searchBar = UISearchBar()
searchBar.placeholder = "Search"
searchBar.autocapitalizationType = .none
searchBar.backgroundImage = UIImage()
(searchBar.value(forKey: "searchField") as? UITextField)?.backgroundColor = UIColor(r: 230, g: 230, b: 230)
searchBar.delegate = self
searchBar.becomeFirstResponder()
let frame = CGRect(x: 0, y: 0, width: 300, height: 44)
searchBar.frame = frame
return searchBar
}()
in viewDidLoad add the customTitleView that holds the searchBar
override func viewDidLoad() {
super.viewDidLoad()
customTitleView.addSubview(searchBar)
navigationItem.titleView = customTitleView
searchBar.becomeFirstResponder()
}
add delegate method to listen for searches
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print(searchText)
}
make sure you present your view controller on the navigation stack
func handleSearchBarButtonItemTapped() {
let controller = SearchDatasourceController()
navigationController?.pushViewController(controller, animated: true)
}
Related
For some design purpose, I need to place an image above the navigation bar.
Apple is pretty strict on not changing Nav Bar Height.. and I won't go there.
It's kind of a big app, so I won't have to change each controller apart.
My controllers are mostly presented in nav controllers, so I thought making an extension of UINavigationController will do the trick (so far it does). Anyone have ideas about this ?
extension UINavigationController {
override open func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 11.0, *) {
// This add space under my nav bar
self.additionalSafeAreaInsets = UIEdgeInsets(top: 80, left: 0, bottom: 0, right: 0)
// Need to find a way to move down my nav and add the logo above
}
}
}
The best way for you to place an image is in the Navigation title:
let logo = UIImage(named: "logo.png")
let imageView = UIImageView(image:logo)
self.navigationItem.titleView = imageView
If you want something more customizable, I would suggest you create a View with a 0 constraint with SafeArea.
Add this views in your xib:
And create your navigationController in your class, like this:
import UIKit
class TopLogoViewController: UIViewController {
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let controller = UINavigationController(rootViewController: ViewController())
controller.navigationBar.backgroundColor = .green
controller.navigationBar.barTintColor = .green
controller.navigationBar.shadowImage = UIImage()
controller.navigationBar.isTranslucent = false
addChild(controller)
containerView.addSubview(controller.view)
controller.view.frame = containerView.bounds
controller.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
controller.didMove(toParent: self)
}
}
How do I get the frame of a navigationItem's titleView in the coordinate system of the viewcontroller's view?
if let navBarHeight = navigationController?.navigationBar.frame.height,
let navBarWidth = navigationController?.navigationBar.frame.width {
myCustomTitleView.frame = CGRect(x: 0, y: 0, width: navBarWidth, height: navBarHeight)
navigationItem.titleView = myCustomTitleView
}
However, when I check myCustomTitleView's frame origin, I get (0, 0).
I then tried to translate this origin to the viewcontroller's view. what I got was (0,-44), which accounts for the navigation bar height but not for the x-offset.
let originInVCView = view.convert(myCustomTitleView.frame.origin, from: myCustomTitleView)
This can't be right as the titleView obviously has an offset (space for the back button).
How do I correctly extract the translated titleView origin?
You want to make sure you have set the navigation item in viewDidLoad() first. Otherwise it will be nil.
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView(image: UIImage(named: "MY_IMAGE"))
navigationItem.titleView = imageView
When done you can get the frame in the VC's viewDidAppear where the view has been laid out:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let nItemFrame = navigationItem.titleView?.frame //<<<---
}
How to add tabbar below navigation bar using the Material's pod by CosmicMind
I was able to achieve this but it was touching the status bar like this
I want this to be below the status bar.
You can do with below code, add 20px StatusBar Height in Top(20)
fileprivate func prepareTabBar() {
tabBar = TabBar()
tabBar.delegate = self
tabBar.dividerColor = Color.grey.lighten4
tabBar.dividerAlignment = .bottom
tabBar.lineColor = Color.indigo.base
tabBar.lineAlignment = .bottom
tabBar.backgroundColor = Color.grey.lighten3
tabBar.buttons = buttons
view.layout(tabBar).horizontally().top(20)
}
You can create custom class of Tabbar and change frame at top:
class CustomTabBarController: UITabBarController {
#IBOutlet weak var myTabBarOutlet: UITabBar!
override func viewDidLoad() {
super.viewDidLoad()
UIApplication.shared.statusBarFrame.size.height
myTabBarOutlet.frame = CGRect(x: 0, y: myTabBarOutlet.frame.size.height, width: myTabBarOutlet.frame.size.width, height: myTabBarOutlet.frame.size.height)
}
There is extra padding top when my app in landscape.
Here is my code when create navigation bar programmatically.
Any advice to remove the padding top when in landscape mode?
let navigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.frame.size.width, 44)
navigationBar.backgroundColor = UIColor.redColor()
navigationBar.delegate = self;
let navigationItem = UINavigationItem()
navigationItem.title = "Title"
let btnLeft = UIButton(frame: CGRectMake(0, 0, 44, 44))
btnLeft.setImage(UIImage(named: “myImage.png"), forState: .Normal)
let leftButton = UIBarButtonItem()
leftButton.customView = btnLeft
navigationItem.leftBarButtonItem = leftButton
navigationBar.items = [navigationItem]
self.view.addSubview(navigationBar)
Snapshot from simulator (Landscape)
Snapshot from simulator (Portrait)
You should easily solve with this setting (in your viewDidLoad for example):
self.edgesForExtendedLayout = UIRectEdge.None
Alternative: You can put it also in your UINavigationController delegate method (if you have setted in your class your navigation delegate UINavigationControllerDelegate ) called willShowViewController:
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
viewController.edgesForExtendedLayout = UIRectEdge.None
}
If you work with UITableView you may also to do:
self.automaticallyAdjustsScrollViewInsets = false
You can also edit directly to the attribute inspector:
If anyone of these approaches don't work, by default your UINavigationBar have different heights based on your orientation. For example, the navigation bar is 44 points in portrait and 32 points in landscape.
A workaround can be:
// Create an IBOutlet of your navigationBar height constraint
#IBOutlet weak var navBarHeight: NSLayoutConstraint
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if self.view.bounds.size.height > self.view.bounds.size.width {
self.navBarHeight.constant = 44
} else {
self.navBarHeight.constant = 32
}
}
i'm trying to keep the UISearchBarDisplayController on top of UItableview in swift
i tried this code (which i convert from objectC to swift) but no luck:
override func scrollViewDidScroll(scrollView: UIScrollView) {
//
var searchBar: UISearchBar = (self.searchDisplayController?.searchBar)!
//
var rect = searchBar.frame
self.searchDisplayController?.searchBar.frame = CGRectMake(0, max(0, scrollView.contentOffset.y), rect.width, rect.height);
}
the search bar still scroll with tableview when i scroll the table. Does anyone can help me to fix it? thanks
i found that i have to make a new view controller , add a tableview and add a searchBar outside of the tableview. and the search bar won't scroll with the tableview
In Swift 2.1, iOS 9.2.1 and Xcode 7.2
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
/* Search controller parameters */
searchController.searchResultsUpdater = self // This protocol allows your class to be informed as text changes within the UISearchBar.
searchController.dimsBackgroundDuringPresentation = false // In this instance,using current view to show the results, so do not want to dim current view.
definesPresentationContext = true // ensure that the search bar does not remain on the screen if the user navigates to another view controller while the UISearchController is active.
let tableHeaderView: UIView = UIView.init(frame: searchController.searchBar.frame)
tableHeaderView.addSubview(searchController.searchBar)
self.tableView.tableHeaderView = tableHeaderView
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
let searchBar:UISearchBar = searchController.searchBar
var searchBarFrame:CGRect = searchBar.frame
if searchController.active {
searchBarFrame.origin.y = 10
}
else {
searchBarFrame.origin.y = max(0, scrollView.contentOffset.y + scrollView.contentInset.top)
}
searchController.searchBar.frame = searchBarFrame
}