IOS Swift how can I include the search function inside another function - ios

I have 2 Controllers and one is a SubView. The MainController has access to the SearchBar property and the SubViewController does not . There is a SearchBar function in the Main Controller that I would like to invoke in the SubView Controller it is this function
// MainController
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
// Stop doing the search stuff
// and clear the text in the search bar
searchBar.text = ""
// Hide the cancel button
searchBar.showsCancelButton = false
searchBar.endEditing(true)
Popup.Close_View()
}
What I am trying to do is wrap that function above in another function like this
func Selected_Location() {
searchBarCancelButtonClicked(SearchBar)
}
so that in my SubView Controller I can do
// SubView Controller
let Select_Close = MainController()
Select_Close.Selected_Location()
I am new to Swift and as stated before how can I get the searchBarCancelButtonClicked function and call it inside another function with ? I have tried doing it in several ways such as
func Selected_Location() {
searchBarCancelButtonClicked(_ searchBar: UISearchBar)
}
func Selected_Location() {
searchBarCancelButtonClicked(searchBar)
}
Again my SubView Controller does not have access to a UISearchBar so I am trying to call the SearchBar close function from within the SubView.

In Subview, keep weak reference of MainViewController, and then call one method of mainviewcontroller searchBarCancel
class MainViewController:UIViewController {
var subViewController: SubViewController
func searchBarCancel(){
searchBarCancelButtonClicked(self.searchBar)
}
func func2(){
subViewController.mainController = self
}
}
class SubViewController:UIView {
weak var mainController: MainViewController?
func Selected_Location() {
mainController!.searchBarCancel()
}
}

Related

SwiftUI calling delegate in UIViewController

This is the first time I am working with SwiftUI, and I have searched for this problem but did not find any working solution, so I would appreciate all help I can get.
I have a UIViewController, in which I present a SwiftUI View through UIHostingController. What I want to achieve is that when I press the button in my SwiftUI view, the action is going to trigger the delegate in UIViewController, alternatively, trigger a function in UIViewController and then from that function trigger the delegate.
class MyViewController: UIViewController {
public var delegate: MyViewControllerDelegate?
let facialView = UIHostingController(rootView: FacialTutorial())
override func viewWillAppear(_ animated: Bool) {
addChild(facialView)
view.addSubview(facialView.view)
setupConstraints()
}
extension MyViewController {
#objc private func buttonPressed() {
delegate?.buttonPressed()
}
}
}
And in my SwiftUI view FacialTutorial
struct FacialTutorial: View {
var body: some View {
VStack() {
Button {
// I want to call delegate?.buttonPressed() action here
} label: {
Text("Press button")
}
}
}
}
EDIT
Okay to be more clear, my ViewController is configuring the page differently for a number of cases. So in practice, I do not initiate the SwiftUI view from viewWillAppear. Rather this is how I do
public var delegate: MyViewControllerDelegate?
let facialView = UIHostingController(rootView: FacialTutorial())
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addChild(facialView)
configureSubviews()
}
private func configureForInstructionMode() {
view.addSubview(facialUIView.view)
setupConstraints()
}
I must have it this way because I need to configure the view differently depending on which mode I am going to configure for. When I declare the facialView inside the viewDidLoad or viewWillAppear, I cannot access the instance in configureForInstructionMode(), or it's value is nil..
You simply need to pass a reference to your MyViewController instance through to MyUIView (Which should probably be MyView since it isn't a subclass of UIView) via its initialiser. You could use a delegation pattern or you could pass a closure and then invoke the delegate method from the closure. The second way is more "Swifty".
(I have moved the code to viewDidLoad since if you have it in viewWillAppear the view may be added multiple times if the view controller appears and disappears and appears again)
class MyViewController: UIViewController {
public var delegate: MyViewControllerDelegate? {
weak var myView: UIHostingController<MyUIView>!
override func viewDidLoad() {
super.viewDidLoad()
let myView = UIHostingController(rootView: MyUIView(buttonHandler: { [weak self] in
self?.delegate?.buttonPressed()
}))
self.myView = myView
view.addSubview(myView.view)
setupConstraints()
}
}
Then, in your SwiftUI view you can declare a property to hold the closure and invoke that closure in your button
struct MyUIView: View {
var buttonHandler: (()->Void)?
var body: some View {
VStack() {
Button {
buttonHandler?()
} label: {
Text("Press button")
}
}
}
}

Making search bar notify the viewController that user is about to search

I am implementing a search feature in my app. The app consists of a view controller and a custom class that handles the search logic. This custom class is called SearchController.
My goal is to make the searchBar notify the view controller when the user is about to begin searching (exactly the same behaviour as the UISearchBarDelegate method searchBarShouldBeginEditing).
Normally, you would just declare searchBarShouldBeginEditing inside SearchController but I am trying to call this method from inside the viewController because I want something in my view to change when this event happens (and thus the viewController should be handling it, not the searchController).
SearchController class:
class SearchController: NSObject, UISearchBarDelegate {
let searchBar = UISearchBar()
var searchButton = UIBarButtonItem? = nil
/* Other irrelevant class properties */
func setup() {
searchBar.delegate = self
/* other setup */
}
}
ViewController class:
class ViewController: UIViewController {
private let searchController = SearchController()
override func viewDidLoad() {
super.viewDidLoad()
searchController.delegate = self
searchController.setup()
}
/* setup a tableview to display results... this part of the implementation works fine */
}
I omitted the majority of these two classes because the search feature already works. The only thing I am struggling with is finding a way to let viewController know when the user is about to begin typing into the search field.
I tried making viewController implement UISearchBarDelegate but I am already making SearchController implement UISearchBarDelegate so why can't I access the delegate methods inside viewController?
I hope I made myself clear, I can clarify this post further if necessary. I have been tearing my hair out trying to figure this out on my own.
Ok, a searchBar cannot have 2 delegates, so you're gonna have to find a way to work around that.
One way to go about this is this:
protocol SearchControllerDelegate: class{
func searchBarDidBeginEditing()
}
class SearchController: NSObject, UISearchBarDelegate {
weak var delegate: SearchControllerDelegate?
private let searchBar = UISearchBar()
func setup() {
searchBar.delegate = self
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
delegate?.searchBarDidBeginEditing()
}
}
class ViewController: UIViewController, SearchControllerDelegate{
var searchController = SearchController()
func setUP(){
self.searchController.delegate = self
}
func searchBarDidBeginEditing() {
/// perform some action here
}
}
You can use UISearchController as rmaddy suggested, implement UISearchResultsUpdating
ViewController class:
class ViewController: UIViewController, UISearchResultsUpdating {
private let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.delegate = self
....
}
// Called when the search bar's text or scope has changed or when the search bar becomes first responder.
func updateSearchResults(for searchController: UISearchController) {
//Do something...
}
}
Or if you really want to implement the search bar logic yourself, you can go with the closure:
SearchController class:
class SearchController: NSObject, UISearchBarDelegate {
let searchBar = UISearchBar()
var searchButton = UIBarButtonItem? = nil
var didBeginSearching: (() -> ())?
/* Other irrelevant class properties */
func setup() {
searchBar.delegate = self
/* other setup */
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
didBeginSearching?()
}
}
ViewController class:
class ViewController: UIViewController {
private let searchController = SearchController()
override func viewDidLoad() {
super.viewDidLoad()
searchController.setup()
searchController.didBeginSearching = { [weak self] in
//Do something...
}
}
}

searchBarSearchButtonClicked not working

I have set the delegate in the viewDidLoad() method and also checked the IB to see if the delegate shows up when using (control + drag) and it does infact show as being hooked up. I deleted it as well and added a new SearchBar entirely in IB and hooked it back up. Nothing seems to do the trick. Any suggestions?
override func viewDidLoad() {
self.searchBar.layer.zPosition = 1
self.searchBar.delegate = self
}
//this is never being called, breakpoint never hits
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
print("searchText \(searchText)")
}
//this is never being called, breakpoint never hits
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
print("search button clicked")
self.firebaseQuery()
}
Is your custom View Controller class set in Interface Builder? I.e. on the Identity Inspector tab, ensure your desired UIViewController subclass is set in the Custom Class section.
Also, try setting a breakpoint in viewDidLoad(). Run the app and if the breakpoint doesn't get hit when you expect, that helps narrow down the problem.
Is it possible you're setting the delegate incorrectly?
In Interface Builder, command-click the search bar, drag the delegate to the yellow ViewController button in the storyboard to set the delegate.
Remove self.searchBar.delegate = self from viewDidLoad()
I'm also not seeing if your viewController Class has properly conformed to UISearchBarDelegate I would add it in an extension to your controller and put all your code relating to it there.
class ViewController: UIViewController{...}
extension ViewController: UISearchBarDelegate {
// implement all searchBar related methods here:
func searchBarButtonClicked(_ searchBar: UISearchBar) {
//capture the string
if let input = searchBar.text {
// do something with string.
print(input)
}
}
I also had a problem with it not responding when I set it up using IB. I finally set it up using code instead of IB, and it worked. Try it this way:
class MySearchTableViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
self.searchController.searchBar.autocapitalizationType = UITextAutocapitalizationType.none
self.navigationItem.searchController = self.searchController
//if this is set to true, the search bar hides when you scroll.
self.navigationItem.hidesSearchBarWhenScrolling = false
//this is so I'm told of changes to what's typed
self.searchController.searchResultsUpdater = self
self.searchController.searchBar.delegate = self
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
//hopefully this gets called when you click Search button
}
func updateSearchResults(for searchController: UISearchController) {
//if you want to start searching on each keystroke, you implement this method. I'm going to wait until they click Search.
}
https://www.reddit.com/r/swift/comments/85o75h/bug_uisearchbar_delegate_methods_not_being_called/dvzcbb4/
Thanks to reddit user #applishish I got the issue
I did not put a underscore in front of the parameter -_-
Thanks all for your help!

Access viewcontroller from button

I have ButtonClass which has buttonTapped method implemented.
The button itself is on the view. I need to add an extra layer when the button is pressed but I don't have access to self in the ButtonClass because self is a button not the view... Basically I need to access self.view found in the viewController but that's another class. If I create an new instance like so viewController() it won't help because it wont' be the same instance.
If you're adding the buttonTapped method from the custom button class then you need to create a delegate, and call the delegate method when the user the buttonTapped method is called.
In your custom button class
protocol YourCustomButtonDelegate {
func changeSomethingInTheView()
}
class YourCustomButtonClass {
var delegate: YourCustomButtonDelegate?
func buttonTapped(_ sender: AnyObject) {
if let delegate = delegate {
delegate.changeSomethingInTheView()
}
}
}
And in the ViewController
class ViewController: UIViewController, CustomCellDelegate {
#IBOutlet weak var button: YourCustomButton!
override func viewDidLoad() {
super.viewDidLoad()
button.delegate = self
}
func changeSomethingInTheView() {
// write your code here...
}
}

SubView click item to show in parent view label

I would like to make a UI that have label, table view and one button click. When click on the button, we pop up a half screen view that have lots of buttons. I want user can still click on the rest of the screen also.
So i use the approach that suggest in the post
How To Present Half Screen Modal View?
Method 2: to animate a UIView which is of size half of the existing view.
Then you have to simply follow animation of the UIView.
Here as it is just a UIView that will be added as subview to existing view, you will be able to touch the rest of the screen.
As i am newbie to the ios and swift, I would like to get some suggestions.
Now i am successfully add as subview and show in the half of the screen.
How can i implement to let subview click button result show on parent view label text?
I am thinking about parent.xib and subview.xib have the same UIVeiwController.swift. Then i can #IBOutlet and #IBAction to the same controller swift file and update the result. But don't know it is the accpetable way to do?
If not, how can the subViewController send result/event to the parent view and update in the parent view component?
You could use delegation. This keeps your view controllers decoupled, i.e. prevents the child from having a reference to its parent, which allows other view controllers to interact with the modal view controller in the same way.
class ParentViewController : UIViewController, ModalViewControllerDelegate {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let modalViewContorller = ModalViewController()
modalViewContorller.delegate = self
self.presentViewController( modalViewContorller, animated: true, completion: nil )
}
func modalViewControllerDidProduceResult( modalViewController: ModalViewController, result: String ) {
self.label.text = result
}
}
protocol ModalViewControllerDelegate {
func modalViewControllerDidProduceResult( modalViewController: ModalViewController, result: String )
}
class ModalViewController: UIViewController {
var delegate: ModalViewControllerDelegate?
#IBAction func buttonClicked( sender: AnyObject? ) {
delegate?.modalViewControllerDidProduceResult( self, result: "Hello!" )
}
}
You could also use a closure, which in Swift provides a more concise syntax.
class ParentViewController : UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let modalViewContorller = ModalViewController()
self.presentViewController( modalViewContorller, animated: true, completion: nil )
modalViewContorller.resultBlock = { (result: String) in
self.label.text = result
}
}
}
class ModalViewController: UIViewController {
var resultBlock: ((String) -> ())?
#IBAction func buttonClicked( sender: AnyObject? ) {
self.resultBlock?( "Hello!" )
}
}

Resources