なんでUIButtonではできて、UIBarButtonItemだとできない?
ジェスチャーの登録はUIViewの拡張クラスでやってるぽい
extension UIView {
@available(iOS 3.2, *)
public var gestureRecognizers: [UIGestureRecognizer]?
@available(iOS 3.2, *)
public func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer)
@available(iOS 3.2, *)
public func removeGestureRecognizer(gestureRecognizer: UIGestureRecognizer)
// called when the recognizer attempts to transition out of UIGestureRecognizerStatePossible if a touch hit-tested to this view will be cancelled as a result of gesture recognition
// returns YES by default. return NO to cause the gesture recognizer to transition to UIGestureRecognizerStateFailed
// subclasses may override to prevent recognition of particular gestures. for example, UISlider prevents swipes parallel to the slider that start in the thumb
@available(iOS 6.0, *)
public func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool
}
UIButtonの継承関係
UIView -> UIControl -> UIButton からなってる
正確にはもっと継承してるんだけど割愛
public class UIButton : UIControl, NSCoding
public class UIControl : UIView
UIBarButtonItemの継承関係
NSObject -> UIBarItem -> UIBarButtonItem からなってる
public class UIBarButtonItem : UIBarItem, NSCoding
public class UIBarItem : NSObject, NSCoding, UIAppearance
つまり、UIBarButtonItem はUIViewを継承してないのでジェスチャーの登録が使えないぽい。
UIBarButtonItemと言えども、ボタンなんだからどうにかできるっしょ
Propertyの確認
public var style: UIBarButtonItemStyle // default is UIBarButtonItemStylePlain
public var width: CGFloat // default is 0.0
public var possibleTitles: Set<String>? // default is nil
public var customView: UIView? // default is nil
public var action: Selector // default is NULL
weak public var target: AnyObject? // default is nil
このcutomViewにジェスチャー登録すれば済むっしょと思いきや、デフォルトはnilのためジェスチャーが認識されない模様。
じゃあ、親のUIBarItemのPropertyは?
public var enabled: Bool // default is YES
public var title: String? // default is nil
public var image: UIImage? // default is nil
@available(iOS 5.0, *)
public var landscapeImagePhone: UIImage? // default is nil
public var imageInsets: UIEdgeInsets // default is UIEdgeInsetsZero
@available(iOS 5.0, *)
public var landscapeImagePhoneInsets: UIEdgeInsets // default is UIEdgeInsetsZero. These insets apply only when the landscapeImagePhone property is set.
public var tag: Int // default is 0
うん、ないね
諦めないお
ぶっちゃけUIViewがないわけないと思うので、調べてみると公開してないUIViewありました。
取得方法
UIBarButtonItemに対してvalueForKey
を行う。
- Swift
let buttonView = self.valueForKey("view")
UIView系のオブジェクトからプライベートなViewを取得する際は、subviewsで順番に見てくやり方もあるのですが、UIBarButtonItemはUIViewを継承していないため、valueForKeyで取得します。
そしてこのViewに対してジェスチャーを登録すればいけそうです!
実装内容
//
// TSKLongPressBarButtonItem.swift
// ToolBar
//
// Created by Toshiki Chiba on 2015/11/14.
// Copyright © 2015年 Toshiki Chiba. All rights reserved.
//
import UIKit
protocol TSKLongPressBarButtonDelegate : class {
func handleLongPressGesture(sender: AnyObject)
}
class TSKLongPressBarButtonItem: UIBarButtonItem, UIGestureRecognizerDelegate {
private var recognizerState: UIGestureRecognizerState = .Possible // Default state
weak var delegate: TSKLongPressBarButtonDelegate?
func setLongTapGesture(allowableMovement: CGFloat, minimumPressDuration: CFTimeInterval,
numberOfTapsRequired: Int, numberOfTouchesRequired: Int, state: UIGestureRecognizerState,
buttonDelegate: TSKLongPressBarButtonDelegate?) {
self.removeLongGesture()
if let buttonView = self.valueForKey("view") {
let longPressGesture = UILongPressGestureRecognizer(target: self, action: "p_handleLongPressGesture:")
longPressGesture.allowableMovement = allowableMovement
longPressGesture.minimumPressDuration = minimumPressDuration
longPressGesture.numberOfTapsRequired = numberOfTapsRequired
longPressGesture.numberOfTouchesRequired = numberOfTouchesRequired
longPressGesture.delegate = self
buttonView.addGestureRecognizer(longPressGesture)
}
self.recognizerState = state
self.delegate = buttonDelegate
}
func removeLongGesture() {
// TODO: Refactor
if let buttonView = self.valueForKey("view") {
if let recognizerList: [UIGestureRecognizer] = buttonView.gestureRecognizers {
for recognizer in recognizerList {
if recognizer.isKindOfClass(UILongPressGestureRecognizer) {
buttonView.removeGestureRecognizer(recognizer)
}
}
}
}
}
func p_handleLongPressGesture(sender: UIGestureRecognizer) {
if sender.state == self.recognizerState {
self.delegate?.handleLongPressGesture(self)
}
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
実行してみた
まとめ
正式に用意されてる方法ではないと思うので使う際は注意が必要かも。
ジェスチャーの細かい設定も簡単にしたいなーと思ったのでいい感じのUIBarButtonカスタムクラスを作りました。
Sample code