I tried to push a child view controller with the searchBar display, there is a flashing gray bar appeared during the animation (see Figure 2), how can I fix that animation? Thank you.
Code:
override func viewDidLoad() {
super.viewDidLoad()
if title == nil {
title = "Title"
}
if #available(iOS 11.0, *) {
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.searchController = searchController
}
definesPresentationContext = true
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let vc = ViewController()
vc.hidesBottomBarWhenPushed = true
navigationController?.pushViewController(vc, animated: true)
}
If you're trying to push the same ViewController() which is embedded with NavigationController, that bar you saw might be the navigationBar of your ViewController().
Try to create a new controller, then push to see whether the bar still exists, like so:
class NewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
}
}
Then in your ViewController's didSelectRowAt, push the NewController, like so:
let newVC = NewController()
navigationController?.pushViewController(newVC, animated: true)
Related
I am trying to get a smooth Searchbar on navigation item on iOS 9, which means I can't use navigationItem.searchController property since its only iOS 11 only.
class SearchContainerViewController: UITableViewController {
let dataSource = ["1", "2", "3", "4", "5"]
override public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
override public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = dataSource[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dismiss(animated: true, completion: nil)
}
}
class SearchViewController: UISearchController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
class MyViewController : UIViewController, UISearchResultsUpdating, UISearchBarDelegate {
lazy var searchButton = UIBarButtonItem(title: "Search", style: UIBarButtonItem.Style.plain, target: self, action: #selector(showSearchBar))
var searchViewController: SearchViewController = {
let container = SearchContainerViewController()
let searchController = SearchViewController(searchResultsController: container)
return searchController
}()
override func viewDidLoad() {
super.viewDidLoad()
setupSearchController()
setupSearchButton()
}
func setupSearchController() {
searchViewController.searchResultsUpdater = self
searchViewController.searchBar.delegate = self
searchViewController.dimsBackgroundDuringPresentation = false
searchViewController.hidesNavigationBarDuringPresentation = false
searchViewController.searchBar.searchBarStyle = .minimal
searchViewController.searchBar.showsCancelButton = true
definesPresentationContext = true
}
#objc func showSearchBar() {
UIView.animate(withDuration: 0.75) {
self.navigationItem.titleView = self.searchViewController.searchBar
self.navigationItem.rightBarButtonItem = nil
self.searchViewController.searchBar.becomeFirstResponder()
}
}
func setupSearchButton() {
UIView.animate(withDuration: 0.75) {
self.navigationItem.titleView = nil
self.navigationItem.rightBarButtonItem = self.searchButton
}
}
// MARK: Conforms to UISearchResultUpdating
public func updateSearchResults(for searchController: UISearchController) { }
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
setupSearchButton()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
view.layoutSubviews()
}
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let newWindow = UIWindow(frame: UIScreen.main.bounds)
let mainViewController = MyViewController()
let navigationController = UINavigationController(rootViewController: mainViewController)
newWindow.backgroundColor = .white
newWindow.rootViewController = navigationController
newWindow.makeKeyAndVisible()
window = newWindow
return true
}
}
Though the result is kinda disappointing since the textview with StatusBar is clipping out of the navigation item context, there is anything im doing wrong and could've done better?
Appreciate your support.
In before people down voting the question for no reasons, I am going to do something different and answer my own question.
The clipping happened because in both situations the height of the navigationItem were different because of they are somewhat stretchable if put big content in titleView (like a searchBar).
I've set the searchBar from the start on the navigationItem and just toogle their isHidden property when it should be done.
#objc private func activateSearch() {
UIView.animate(withDuration: 0.75) {
self.navigationItem.titleView?.isHidden = false
self.navigationItem.rightBarButtonItem = nil
self.searchController.isActive = true
}
}
private func deactivateSearch() {
UIView.animate(withDuration: 0.75) {
self.navigationItem.titleView?.isHidden = true
self.navigationItem.rightBarButtonItem = self.searchButton
self.searchController.isActive = false
}
}
I'm unable to transition to my View Controller without having dismissed my Search Controller. My search controller is embedded in my TableView Header.
Is there a way of transitioning without having dismissed my Search Controller first?
Here is some code:
class ViewProfileViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource, UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource, UISearchBarDelegate, UISearchResultsUpdating {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
searchController.searchBar.searchBarStyle = .minimal
searchController.searchBar.placeholder = "Search City"
searchController.searchBar.showsCancelButton = true
searchController.searchBar.delegate = self
searchController.searchBar.backgroundColor = UIColor.white
self.myTable.tableHeaderView = searchController.searchBar
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
// Get rid of searchController
searchController.searchBar.endEditing(true)
searchController.isActive = false
searchController.dismiss(animated: true) { /* */ }
// Deselect row
tableView.deselectRow(at: indexPath, animated: true)
// Present your VC here
let mViewController = MController()
let navController = UINavigationController(rootViewController:
mViewController)
present(navController,animated: true, completion: nil)
}
}
I have a search bar that is connected to a table view. It works exactly as I want when the search bar is not active but cell selection is disabled when the search bar is active. I've debugged it and didSelectRowAtIndexPath is not even being called when I select a row when search is active. What could be causing this?
Here's the relevant code:
class FabricsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchControllerDelegate, UISearchDisplayDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.definesPresentationContext = false
searchController.hidesNavigationBarDuringPresentation = false
myTableView.tableHeaderView = searchController.searchBar
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if searching {
searching = false
searchBar?.resignFirstResponder()
FirebaseClient.sharedInstance.joinFabric(uid: self.appDelegate.uid!, fabricKey: allFabrics[indexPath.row].key)
updateFabricList()
} else {
appDelegate.selectedFabricKey = joinedFabrics[indexPath.row].key
performSegue(withIdentifier: "fabricSelected", sender: self)
}
myTableView.deselectRow(at: indexPath, animated: false)
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
if let allFabrics = allFabrics {
filteredFabrics = allFabrics.filter { fabric in
return (fabric.name.lowercased().contains(searchText.lowercased()))
}
myTableView.reloadData()
myTableView.setContentOffset(CGPoint.zero, animated: false)
}
}
}
extension FabricsViewController: UISearchResultsUpdating {
func updateSearchResults(for: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
}
Please check the text for your method name.
Make sure that you are using method name as below:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
You have set the delegate of searchBar to self. Are you activating any tap gestures when search becomes active? This tap gesture can interrupt your didSelectRowAtIndexPath method.
For those who are facing same issue, according Apple's documentation you should have following code for add searchController:
if #available(iOS 11.0, *) {
navigationItem.searchController = searchController
} else {
self.yourTableView.tableHeaderView = searchController.searchBar
}
I have a table view inside a UIViewController with editing enabled for deleting rows. Swiping from the right lets me delete rows and I have the edit button on the navigation bar but it doesn't actually do anything except switch from saying Edit to Done.
Here is how I'm creating my table view.
override func viewWillAppear(animated: Bool) {
for view in self.view.subviews {
view.removeFromSuperview()
}
if sharedCart.shoppingCart.isEmpty {
self.navigationItem.rightBarButtonItem = nil
isEmptyLabel = UILabel(frame: CGRectMake(self.view.frame.width / 2, self.view.frame.height / 2, self.view.frame.width, self.view.frame.height))
isEmptyLabel.center = self.view.center
isEmptyLabel.text = "Your cart is empty."
isEmptyLabel.textAlignment = .Center
isEmptyLabel.textColor = UIColor.whiteColor()
isEmptyLabel.font = UIFont(name: "Helvetica-Light", size: 20.0)
self.view.addSubview(isEmptyLabel)
} else {
isEmptyLabel.removeFromSuperview()
total = 0
let editItem = self.editButtonItem()
self.navigationItem.rightBarButtonItem = editItem
tableView = UITableView(frame: self.view.frame, style: .Grouped)
tableView.delegate = self
tableView.dataSource = self
tableView.backgroundColor = UIColor.clearColor()
tableView.contentInset = UIEdgeInsetsMake(44, 0, 0, 0)
tableView.registerNib(UINib(nibName: "CartCell", bundle: nil), forCellReuseIdentifier: "passCartCell")
tableView.registerNib(UINib(nibName: "CartFooterView", bundle: nil), forHeaderFooterViewReuseIdentifier: "cartFooter")
self.view.addSubview(tableView)
}
And I use these methods for editing.
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
sharedCart.shoppingCart.removeAtIndex(indexPath.row)
self.viewWillAppear(true)
}
}
Cedric Michael's answer almost works, but it disables editButtonItem's automatic, animated toggling between the Edit and Done title & associated state. The better fix is this:
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tableView.setEditing(editing, animated: animated)
}
Also, if you want to hide the Edit button for an empty shopping cart, it would be better form to set navigationItem.rightBarButtonItem = editButtonItem in viewDidLoad() and simply set navigationItem.rightBarButtonItem.isHidden to true or false in viewWillAppear() according to the shopping cart.
For Swift 3, implement this in your UIViewController class:
override func setEditing(_ editing: Bool, animated: Bool) {
if(editing && !tableView.isEditing){
tableView.setEditing(true, animated: true)
}else{
tableView.setEditing(false, animated: true)
}
}
(This is not necessary for a UITableViewController class).
You should also set your editButtonItem before:
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = editButtonItem
}
Then it works! ツ
In order for your table to toggle between edit and normal mode on button tap you need to implement
func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
if self.tableView.editing {
return UITableViewCellEditingStyle.Delete
}
else{
return UITableViewCellEditingStyle.None
}
}
And on button tap or IBAction of button :)
#IBAction func editButtonTapped(sender: AnyObject) {
var button = sender as! UIButton
if button.titleLabel!.text! == "Edit" {
self.tableView.editing = true
button.setTitle("Done", forState: UIControlState.Normal)
}
else{
self.tableView.editing = false
button.setTitle("Edit", forState: UIControlState.Normal)
}
}
EDIT
Just realised you have bar button item on navigation bar rather then plain UIButton :)
So you can modify the IBAction as below :) Make sure the you select System Item for UIBarButton item as Custom and set the title as Edit
#IBAction func editButtonTapped(sender: AnyObject) {
let button = sender as! UIBarButtonItem
if button.title! == "Edit" {
self.tableView.editing = true
button.title = "Done"
}
else{
self.tableView.editing = false
button.title = "Edit"
}
}
This should do the job :) Happy coding :)
I have master-detail application, where I use UISearchController to filter cells in UITableViewController (master). I have problem with disappearing UISearchBar after segue. Steps to reproduce problem:
Run app in portrait mode
Touch SearchBar and type search query
Touch one of the cells in TableView and segue to detail controller
While in detail controller, change orientation of the device to landscape mode
Segue back to master. Now SearchBar is partially hidden behind Navigation Bar, even after chaning orientation back to portrait.
My Master controller's MWE:
import UIKit
class MasterViewController: UITableViewController, UISearchResultsUpdating {
var data = ["First", "Second", "Third", "Fourth"]
var searchController: UISearchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
configureSearchController()
}
override func viewWillAppear(animated: Bool) {
self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed
super.viewWillAppear(animated)
}
// MARK: - Search Controller
private func configureSearchController() {
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.sizeToFit()
self.definesPresentationContext = true
self.tableView.tableHeaderView = searchController.searchBar
self.tableView.contentOffset = CGPoint(x: 0, y: searchController.searchBar.frame.height)
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let content = data[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.text = content
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let content = data[indexPath.row]
cell.textLabel!.text = content
return cell
}
}
Screenshots: