How can you add a UIGestureRecognizer to a UIBarButtonItem as in the common undo/redo UIPopoverController scheme on iPad apps?

后端 未结 15 1531
被撕碎了的回忆
被撕碎了的回忆 2020-12-07 12:36

Problem

In my iPad app, I cannot attach a popover to a button bar item only after press-and-hold events. But this seems to be standard for

相关标签:
15条回答
  • 2020-12-07 12:41

    Option 1 is indeed possible. Unfortunately it's a painful thing to find the UIView that the UIBarButtonItem creates. Here's how I found it:

    [[[myToolbar subviews] objectAtIndex:[[myToolbar items] indexOfObject:myBarButton]] addGestureRecognizer:myGesture];
    

    This is more difficult than it ought to be, but this is clearly designed to stop people from fooling around with the buttons look and feel.

    Note that Fixed/Flexible spaces are not counted as views!

    In order to handle spaces you must have some way of detecting them, and sadly the SDK simply has no easy way to do this. There are solutions and here are a few of them:

    1) Set the UIBarButtonItem's tag value to it's index from left to right on the toolbar. This requires too much manual work to keep it in sync IMO.

    2) Set any spaces' enabled property to NO. Then use this code snippet to set the tag values for you:

    NSUInteger index = 0;
    for (UIBarButtonItem *anItem in [myToolbar items]) {
        if (anItem.enabled) {
            // For enabled items set a tag.
            anItem.tag = index;
            index ++;
        }
    }
    
    // Tag is now equal to subview index.
    [[[myToolbar subviews] objectAtIndex:myButton.tag] addGestureRecognizer:myGesture];
    

    Of course this has a potential pitfall if you disable a button for some other reason.

    3) Manually code the toolbar and handle the indexes yourself. As you'll be building the UIBarButtonItem's yourself, so you'll know in advance what index they'll be in the subviews. You could extend this idea to collecting up the UIView's in advance for later use, if necessary.

    0 讨论(0)
  • 2020-12-07 12:44

    You also can simply do this...

    let longPress = UILongPressGestureRecognizer(target: self, action: "longPress:")
    navigationController?.toolbar.addGestureRecognizer(longPress)
    
    func longPress(sender: UILongPressGestureRecognizer) {
    let location = sender.locationInView(navigationController?.toolbar)
    println(location)
    }
    
    0 讨论(0)
  • 2020-12-07 12:45

    Until iOS 11, let barbuttonView = barButton.value(forKey: "view") as? UIView will give us the reference to the view for barButton in which we can easily add gestures, but in iOS 11 the things are quite different, the above line of code will end up with nil so adding tap gesture to the view for key "view" is meaningless.

    No worries we can still add tap gestures to the UIBarItems, since it have a property customView. What we can do is create a button with height & width 24 pt(according to Apple Human Interface Guidelines) and then assign the custom view as the newly created button. The below code will help you perform one action for single tap and another for tapping bar button 5 times.

    NOTE For this purpose you must already have a reference to the barbuttonitem.

    func setupTapGestureForSettingsButton() {
          let multiTapGesture = UITapGestureRecognizer()
          multiTapGesture.numberOfTapsRequired = 5
          multiTapGesture.numberOfTouchesRequired = 1
          multiTapGesture.addTarget(self, action: #selector(HomeTVC.askForPassword))
          let button = UIButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
          button.addTarget(self, action: #selector(changeSettings(_:)), for: .touchUpInside)
          let image = UIImage(named: "test_image")withRenderingMode(.alwaysTemplate)
          button.setImage(image, for: .normal)
          button.tintColor = ColorConstant.Palette.Blue
          settingButton.customView = button
          settingButton.customView?.addGestureRecognizer(multiTapGesture)          
    }
    
    0 讨论(0)
  • 2020-12-07 12:46

    This is an old question, but it still comes up in google searches, and all of the other answers are overly complicated.

    I have a buttonbar, with buttonbar items, that call an action:forEvent: method when pressed.

    In that method, add these lines:

    bool longpress=NO;
    UITouch *touch=[[[event allTouches] allObjects] objectAtIndex:0];
    if(touch.tapCount==0) longpress=YES;
    

    If it was a single tap, tapCount is one. If it was a double tap, tapCount is two. If it's a long press, tapCount is zero.

    0 讨论(0)
  • 2020-12-07 12:52

    I know it is not the best solution, but I am going to post a rather easy solution that worked for me.

    I have created a simple extension for UIBarButtonItem:

    fileprivate extension UIBarButtonItem {
        var view: UIView? {
            return value(forKey: "view") as? UIView
        }
        func addGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer) {
            view?.addGestureRecognizer(gestureRecognizer)
        }
    }
    

    After this, you can simply add your gesture recognizers to the items in your ViewController's viewDidLoad method:

    @IBOutlet weak var myBarButtonItem: UIBarButtonItem!
    
    func setupLongPressObservation() {
        let recognizer = UILongPressGestureRecognizer(
            target: self, action: #selector(self.didLongPressMyBarButtonItem(recognizer:)))
        myBarButtonItem.addGestureRecognizer(recognizer)
    }
    
    0 讨论(0)
  • 2020-12-07 12:52

    Ready for use UIBarButtonItem subclass:

    @objc protocol BarButtonItemDelegate {
        func longPress(in barButtonItem: BarButtonItem)
    }
    
    class BarButtonItem: UIBarButtonItem {
        @IBOutlet weak var delegate: BarButtonItemDelegate?
        private let button = UIButton(type: .system)
    
        override init() {
            super.init()
            setup()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            setup()
        }
    
        private func setup() {
            let recognizer = UILongPressGestureRecognizer(
                target: self,
                action: #selector(longPress)
            )
            button.addGestureRecognizer(recognizer)
            button.setImage(image, for: .normal)
            button.tintColor = tintColor
            customView = button
        }
    
        override var action: Selector? {
            set {
                if let action = newValue {
                    button.addTarget(target, action: action, for: .touchUpInside)
                }
            }
            get { return nil }
        }
    
        @objc private func longPress(sender: UILongPressGestureRecognizer) {
            if sender.state == .began {
                delegate?.longPress(in: self)
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题