lumanmann / test-realm

Test on applying Realm in project
0 stars 0 forks source link

Solution to #4 #5

Open lumanmann opened 5 years ago

lumanmann commented 5 years ago

When click on the Done button in the tool bar, there is no response.

Attempt 1

Inspecting maybe the selector is somehow lost when passing to the button Tried to open a Custom Toolbar class to see if it helps ViewController:

 func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
        if searchBarTextField == nil {
            // get the tf layer
            for v in (searchBar.subviews[0]).subviews {
                if let tf = v as? UITextField {
                    searchBarTextField = tf
                    break
                }
            } 
        }
        if selectedScope == 3 {
            self.searchBarTextField!.inputView = datePicker
            self.searchBarTextField!.inputAccessoryView = CustomToolBar(selector: #selector(self.dismissDatePicker))

        } else {

            self.searchBarTextField?.inputView = UITextField().inputView
            self.searchBarTextField!.inputAccessoryView = nil

        }

        searchBar.reloadInputViews()

        self.selectedScope = selectedScope

    }

CustomToolBar:

class CustomToolBar: UIToolbar {

    var handler: (() -> Void)?

    convenience init(selector: Selector) {
        self.init(frame: .zero)

        let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.plain, target: self, action: selector)
        let spaceItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)

        setItems([ spaceItem, doneButton], animated: false)

    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        barStyle = UIBarStyle.default
        isTranslucent = true
        sizeToFit()
        isUserInteractionEnabled = true

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @objc func doneClicked() {
        handler?()
    }

}

Soon I received a error when running the app: It appearances to be a problem of passing the selector. The reason seems to be because the selector pass to view inside view. Responder chain cannot get the right selector from the superview's superview. Therefore there is no response or crash.

Attempt 2

As we cannot pass a selector to view inside view, I choose to use a callback instead.

Amended my code as follow:

class CustomToolBar: UIToolbar {

    var handler: (() -> Void)?

    convenience init(selector: Selector) {
        self.init(frame: .zero)

        let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.plain, target: self, action: selector)
        let spaceItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)

        setItems([ spaceItem, doneButton], animated: false)

    }

    convenience init() {
        self.init(frame: .zero)

        let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.plain, target: self, action: #selector(doneClicked))
        let spaceItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)

        setItems([ spaceItem, doneButton], animated: false)

    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        barStyle = UIBarStyle.default
        isTranslucent = true
        sizeToFit()
        isUserInteractionEnabled = true

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @objc func doneClicked() {
        handler?()
    }

}

ViewController:

 func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
        if searchBarTextField == nil {
            // get the tf layer
            for v in (searchBar.subviews[0]).subviews {
                if let tf = v as? UITextField {
                    searchBarTextField = tf
                    break
                }
            }

        }
        if selectedScope == 3 {
            self.searchBarTextField!.inputView = datePicker
            let toolbar = CustomToolBar()
            toolbar.handler = { [unowned self] in
                self.dismissDatePicker()
            }
            self.searchBarTextField!.inputAccessoryView = toolbar

        } else {

            self.searchBarTextField?.inputView = UITextField().inputView
            self.searchBarTextField!.inputAccessoryView = nil

        }

        searchBar.reloadInputViews()

        self.selectedScope = selectedScope

    }

TODO

Although callback can work, maybe better to figure out how to pass selector through views hierarchy. Some article may help: https://stackoverflow.com/questions/55555335/unrecognized-selector-sent-to-instance https://www.jianshu.com/p/60c251712df7 https://medium.com/@nguyenminhphuc/how-to-pass-ui-events-through-views-in-ios-c1be9ab1626b https://medium.com/ios-os-x-development/understanding-cocoa-and-cocoa-touch-responder-chain-12fe558ebe97

lumanmann commented 5 years ago

Finally figure out the cause of #4

The primary cause the target is pointed to Toolbar instant instead of the view controller, where the selector is. If change the target to view controller, it will work totally fine.