Swift - UISearchController Does Not Activate - No Cursor - No Keyboard - swift

I have a UICollectionView with a header view inside of it and a UISearchController in the title of the navigation bar. This code has worked for other views yet does not work for this UICollectionView. I wanted to throw this out to the SO community to see if they can help me solve this problem.
class ViewBusinessProfile: UICollectionViewController, UICollectionViewDelegateFlowLayout, CLLocationManagerDelegate, UISearchControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
#IBOutlet var collectionViews: UICollectionView!
var searchController : UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchResultsUpdater = self
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.dimsBackgroundDuringPresentation = true
self.searchController.obscuresBackgroundDuringPresentation = false
self.searchController.searchBar.placeholder = "search products..."
//self.searchController.automaticallyShowsCancelButton = false
self.searchController.definesPresentationContext = true
searchController.searchBar.becomeFirstResponder()
navigationItem.titleView = searchController.searchBar
collectionViews.dataSource = self
collectionViews.delegate = self
}
func updateSearchResults(for searchController: UISearchController) {
}
What can I do as far as debugging or anything to try to track down this problem? I have re-created the view in a new UICollectionView and the same problem occurs. The SearchBar is visible but not active to touch yet Back button works on the navigation bar.
These three views all have UISearchControllers on them and I segue between them. The one all the way on the right is where this problem is occurring. The same UISearchController code is used on the left two views and its works perfectly.

Based on what I saw in this related solution, I came up with this code:
import UIKit
class SecondViewController: UIViewController, UISearchControllerDelegate, UISearchBarDelegate {
var searchController : UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
addNavigationBarItems()
}
func addNavigationBarItems() {
self.searchController = searchControllerWith(searchResultsController: nil)
navigationItem.titleView = self.searchController.searchBar
self.definesPresentationContext = true
}
func searchControllerWith(searchResultsController: UIViewController?) -> UISearchController {
let searchController = UISearchController(searchResultsController: searchResultsController)
searchController.delegate = self
// searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
searchController.hidesNavigationBarDuringPresentation = false
// dimsBackgroundDuringPresentation is deprecated as of iOS 12.0
searchController.dimsBackgroundDuringPresentation = true
searchController.searchBar.searchBarStyle = .prominent
searchController.searchBar.backgroundImage = UIImage()
searchController.searchBar.barTintColor = UIColor(red: 10/255, green: 150/255, blue: 255/255, alpha: 1) //rgb(10, green: 150, blue: 255)
return searchController
}
func updateSearchResults(for searchController: UISearchController) {
}
}
This assumes a navigation bar already exists in the parent view controller (i.e. you don't have to create a new NavigationBar), and results should come out looking like this:

Related

How to add SearchController to UITableView Swift?

Im added searchController to tableView header and when table view havent left and right padding all work ok but when im added padding to tableview when im click on search field - searchBar go up and i dont know why and where is the mistake?
it doesn't matter if im set padding on xib or programmatically
#IBOutlet weak var tableView: UITableView!
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.tableFooterView = UIView()
configureSearchController()
}
func configureSearchController() {
definesPresentationContext = true
searchController.searchResultsUpdater = self
searchController.searchBar.placeholder = "Search"
searchController.hidesNavigationBarDuringPresentation = false
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
searchController.searchBar.barTintColor = .white
tableView.tableHeaderView = searchController.searchBar
}
Im was try added searchController on few ways but always the same results like on screens above.
i also try -> Link of the: answer

Swift: deallocate modally presented view controller

I have a modally presented SearchviewController that contains a UISearchController.
When swiping down it gets deallocated, but only if the searchControllers searchBar is not in editing mode. Only if I press its cancel button in advance, it gets deallocated.
How can I make sure it gets deallocated, even when in editing mode? There are definitely no strong self references within any closures...
Presenting ViewController:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
addButton()
}
func addButton() {
let mediumConfiguration = UIImage.SymbolConfiguration(scale: .large)
var checkButtonImage = UIImage(systemName: "plus", withConfiguration: mediumConfiguration)
checkButtonImage = checkButtonImage?.withTintColor(.label)
let button = UIButton(type: .contactAdd)
button.addTarget(self, action: #selector(onAddViewControllerButtonClicked(sender:)), for: .touchUpInside)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
}
#objc func onAddViewControllerButtonClicked(sender: UIButton) {
let viewController = SearchViewController()
viewController.view.backgroundColor = .secondarySystemBackground
let navigationController = UINavigationController()
navigationController.viewControllers = [viewController]
self.present(navigationController, animated: true)
}
}
Presented ViewController:
class SearchViewController: UIViewController, UISearchBarDelegate, UISearchResultsUpdating {
override func viewDidLoad() {
super.viewDidLoad()
configureSearchController()
}
var searchController: UISearchController?
func configureSearchController() {
//search
searchController = UISearchController(searchResultsController: nil)
searchController?.searchResultsUpdater = self
searchController?.searchBar.delegate = self
searchController?.hidesNavigationBarDuringPresentation = false
searchController?.searchBar.searchBarStyle = .minimal
searchController?.searchBar.keyboardType = .webSearch
self.navigationItem.searchController?.searchBar.backgroundColor = .clear
self.navigationItem.searchController = searchController
self.navigationItem.hidesSearchBarWhenScrolling = false
self.definesPresentationContext = true
self.navigationItem.searchController = searchController
}
func updateSearchResults(for searchController: UISearchController) {
return
}
//check deallocation
deinit { print("\(NSStringFromClass(type(of: self))): deallocated") }
}
Can you help with that?
Thank you in advance!
Adding
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.navigationItem.searchController = nil
}
to SearchViewController fixes the problem for me, but admittedly I have no idea as to why this is necessary.

How to navigate from one View Controller to the other?

I want to navigate from one View Controller to another.
let vc = SecondViewController()
I have tried until now :
vc.modalPresentationController = .fullScreen
self.present(vc, animated: true) //self refers to the main view controller
Im trying to open a new ViewController when the users manages to register or to log in.I am new to software developing, and I want to ask, is this the best method to navigate from one ViewController to another, im asking because as I can see the mainViewController is not deinit(). I have found other similar questions and tried the answers, the problem is with the:
self.navigationController?.pushViewController
it doesn't work because I don't have any storyboard.
The question is it is right to navigate as explained above?
Thanks,
Typically when you are doing login you would use neither push or present. There are multiple ways of handling this, but the easiest is to embed in some parent (root) VC. Here is an example:
class ViewController: UIViewController {
private var embeddedViewController: UIViewController! {
didSet {
// https://developer.apple.com/documentation/uikit/view_controllers/creating_a_custom_container_view_controller
// Add the view controller to the container.
addChild(embeddedViewController)
view.addSubview(embeddedViewController.view)
// Create and activate the constraints for the child’s view.
embeddedViewController.view.translatesAutoresizingMaskIntoConstraints = false
embeddedViewController.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
embeddedViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
embeddedViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
embeddedViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// Notify the child view controller that the move is complete.
embeddedViewController.didMove(toParent: self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let loginVC = LoginViewController()
loginVC.delegate = self
embeddedViewController = loginVC
}
}
extension ViewController: LoginDelegate {
func didLogin() {
embeddedViewController = MainViewController()
}
}
protocol LoginDelegate: AnyObject {
func didLogin()
}
class LoginViewController: UIViewController {
private lazy var loginButton: UIButton = {
let button = UIButton()
button.setTitle("Login", for: .normal)
button.addTarget(self, action: #selector(didTapLoginButton), for: .touchUpInside)
return button
}()
weak var delegate: LoginDelegate?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(loginButton)
view.backgroundColor = .red
loginButton.translatesAutoresizingMaskIntoConstraints = false
loginButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
loginButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
#objc private func didTapLoginButton() {
delegate?.didLogin()
}
}
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
}
}

UISeachController Delegate methods not called, searchbar can't become first responder

I'm having some difficulty getting the searchbar in my searchcontroller to become firstResponder. I've noticed the delegate methods are not being called, but the searchbar works as intended when I'm typing to filter a list of users.
Definition of searchcontroller:
lazy var searchController: UISearchController = {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search"
return searchController
}()
Setting it up:
private func setupSearchController() {
self.navigationItem.searchController = searchController
searchController.definesPresentationContext = true
searchController.delegate = self
searchController.isActive = true
searchController.searchBar.delegate = self
searchController.searchBar.becomeFirstResponder()
}
I tried this suggestion from another SO question but the delegate method isn't being called:
func didPresentSearchController(searchController: UISearchController) {
UIView.animate(withDuration: 0.1, animations: { () -> Void in }) { (completed) -> Void in
searchController.searchBar.becomeFirstResponder()
}
}
The problem is you are trying to access UI element(searchbarcontroller) before UI is completely loaded.
This can be done in 2 ways
Use main queue to show keypad
private func setupSearchController() {
self.navigationItem.searchController = searchController
searchController.definesPresentationContext = true
searchController.delegate = self
searchController.isActive = true
searchController.searchBar.delegate = self
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}
With this approach keypad will be only show at one time in viewDidLoad
Show keypad in viewDidAppear
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}
With this approach keypad will be always shown whenever screen appears.

UISearchController - Black Rectangle

I'm implementing a simple tableViewController with UISearchcontroller.
The problem is that every time that I press the search field then a black rectangle appear right after the keyboard shows up.
I also tried to use definesPresentationContext and searchBarStyle but it keep showing the rectangle. On the other hand, it looks like doesn't happen in the simulator since there is no keyboard.
Update: Below some photos.
ViewController:
class ListGlobalViewController: UITableViewController, StoryboardSceneBased, ViewModelBased {
static var sceneStoryboard: UIStoryboard = UIStoryboard(name: "DataSelectorViewController", bundle: Bundle.main)
// --------------------
// MARK: - Properties
// --------------------
var viewModel: ListGlobalViewModel!
private let disposeBag = DisposeBag()
private let searchController: UISearchController = {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.searchBarStyle = .minimal
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = true
return searchController
}()
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
configure(with: viewModel)
navigationItem.largeTitleDisplayMode = .automatic
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.searchController.isActive = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
// --------------------
// MARK: - Functions
// --------------------
func configure(with viewModel: ListGlobalViewModel) {
self.tableView.delegate = nil
self.tableView.dataSource = nil
viewModel.outputs.listDataObservable.bind(to: self.tableView.rx.items(cellIdentifier: "listGlobalCell", cellType: ListGlobalCell.self)) { (index, model, cell) in
cell.model = model
}.disposed(by: disposeBag)
searchController.searchBar.rx.text.filterNil().throttle(1, scheduler: MainScheduler.instance).distinctUntilChanged().subscribe(viewModel.inputs.searchBarObservable).disposed(by: disposeBag)
}
}
Navigation Default Values:
let controller = UINavigationController()
controller.navigationBar.isTranslucent = false
controller.navigationBar.prefersLargeTitles = true
controller.definesPresentationContext = true
controller.navigationBar.titleTextAttributes = [ NSAttributedString.Key.font: UIFont.bold(size: 24), NSAttributedString.Key.foregroundColor: UIColor.black ]
if #available(iOS 11, *) {
controller.navigationBar.largeTitleTextAttributes = [ NSAttributedString.Key.font: UIFont.bold(size: 33), NSAttributedString.Key.foregroundColor: UIColor.black ]
}
return controller
Pictures:
Bugged
Use UITextField instead if UISearchBar. You can give any design to uitextfield. UISearch bar has limited designable attributes. Also you can reach any search function with textfield.