【发布时间】:2013-12-23 09:20:54
【问题描述】:
我试图模仿键盘的长按提示 UIButton 控件的字母。我要做的是长按 UIButton,在按住按钮后显示 3 个新按钮并选择这 3 个新按钮之一。就像键盘字母建议一样。
我该怎么做?任何的想法? 谢谢
【问题讨论】:
标签: ios iphone uibutton uigesturerecognizer
我试图模仿键盘的长按提示 UIButton 控件的字母。我要做的是长按 UIButton,在按住按钮后显示 3 个新按钮并选择这 3 个新按钮之一。就像键盘字母建议一样。
我该怎么做?任何的想法? 谢谢
【问题讨论】:
标签: ios iphone uibutton uigesturerecognizer
你有UIControl 只是为了那个UILongPressGestureRecognizer
并且可以通过minimumPressDuration属性设置发稿时间
如果您希望UIButton 上的这种行为,您必须将此手势识别器添加到按钮并处理第一次调用(单击)。
【讨论】:
您在 UIControl 上有一套完整的事件(UIButton 是它的子类)来处理您需要的所有触摸事件:
enum {
UIControlEventTouchDown = 1 << 0,
UIControlEventTouchDownRepeat = 1 << 1,
UIControlEventTouchDragInside = 1 << 2,
UIControlEventTouchDragOutside = 1 << 3,
UIControlEventTouchDragEnter = 1 << 4,
UIControlEventTouchDragExit = 1 << 5,
UIControlEventTouchUpInside = 1 << 6,
UIControlEventTouchUpOutside = 1 << 7,
UIControlEventTouchCancel = 1 << 8,
UIControlEventValueChanged = 1 << 12,
UIControlEventEditingDidBegin = 1 << 16,
UIControlEventEditingChanged = 1 << 17,
UIControlEventEditingDidEnd = 1 << 18,
UIControlEventEditingDidEndOnExit = 1 << 19,
UIControlEventAllTouchEvents = 0x00000FFF,
UIControlEventAllEditingEvents = 0x000F0000,
UIControlEventApplicationReserved = 0x0F000000,
UIControlEventSystemReserved = 0xF0000000,
UIControlEventAllEvents = 0xFFFFFFFF
};
UIControlEventTouchDown 将在您触摸按钮时触发UIControlEventTouchDownRepeat 将触发(请注意,此事件将触发多次,因此您应该只处理第一个) - 在这里您应该显示弹出框UIControlEventTouchDragExit 将在您将手指从按钮中拖出时触发 - 您应该在此处隐藏弹出框UIControlEventTouchDragEnter 将在您将手指拖入按钮时触发 - 在这里您应该显示弹出框UIControlEventTouchUpInside、UIControlEventTouchUpOutside 和 UIControlEventTouchCancel 将触发 - 您应该在此处隐藏弹出框更新
不过,您将需要实现一些逻辑来处理在弹出框内拖动手指(因为您将从按钮中拖出)。
【讨论】:
这已经很老了,但是因为我遇到了同样的问题,所以我会提出我的解决方案。
我的键盘由类 Keyboard 布局和创建,派生自 UIView 每个字母按钮都是从 UIButton 派生的 LetterButton 类。 键盘实现了一个协议,该协议处理来自按钮的 KeyPressed 事件。
对于主键盘的每个按钮,都会应用一个 UILongPressGestureRecognizer:
let longTouchRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(onButtonLongPressed))
longTouchRecognizer.cancelsTouchesInView = false
button.addGestureRecognizer(longTouchRecognizer)
button.delegate = self
与
@objc func onButtonLongPressed (_ sender: UIGestureRecognizer)
{
if (sender.state == .began)
{
guard let tag = sender.view?.tag else { return }
createPopupView(button: buttons[tag])
}
}
必须将 cancelsTouchesInView 设置为 false,否则我们将不会收到任何进一步的事件!
在一个按钮上长按时,会创建一个弹出视图,在所触摸的按钮上方有一个或多个按钮。我们可以直接从触摸的按钮滑动到这些按钮。
LetterButton类的实现:
class LetterButton : UIButton
{
var delegate : LetterButtonDelegate?
var isInside = false
这是从键盘类调用的:
func setIsInside(val: Bool)
{
if (val)
{
if (!isInside)
{
setBackgroundColor(UIColor.lightGray, for: .normal)
}
}
else
{
if (!isInside)
{
setBackgroundColor(UIColor.white, for: .normal)
}
}
isInside = val
}
其实我这里只需要Keyboard类的按钮
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
if let touch = touches.first
{
let point = touch.location(in: self)
delegate?.onBegan(button: self, point: point)
}
super.touchesBegan(touches, with: event)
}
只要我们将触摸移到按钮之外,移动信息就会发送到Keyboard类:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
{
if let touch = touches.first
{
let point = touch.location(in: self)
if !bounds.contains(point)
{
delegate?.onMoved(point: convert(point, to: superview))
return
}
}
super.touchesMoved(touches, with: event)
}
这是实际处理按钮字母的地方
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
{
if let touch = touches.first
{
let point = touch.location(in: self)
delegate?.onEnded(point: convert(point, to: superview))
}
super.touchesEnded(touches, with: event)
}
}
正如我们所见,LetterButton 类建立了以下需要由 Keyboard 类实现的协议:
protocol LetterButtonDelegate
{
func onBegan(button: LetterButton, point: CGPoint)
func onMoved(point: CGPoint)
func onEnded(point: CGPoint)
}
Keyboard 类中的协议实现如下:
最初被触摸的按钮存储在这里
func onBegan(button: LetterButton, point: CGPoint)
{
buttonPressed = button
}
为我们滑动的按钮处理背景颜色变化
func onMoved(point: CGPoint)
{
let _ = findPopupButton(point: point)
}
触摸结束的处理
func onEnded(point: CGPoint)
{
// Check if touch ended on a popup button
if let button = findPopupButton(point: point)
{
// yes, let the keyboard process the key
delegate?.KeyPressed(key: button.title(for: .normal)!)
button.setIsInside(val: false)
// remove popupbuttons
popupView?.removeFromSuperview()
popupView = nil
}
else
{
// remove popup buttons if touch ended anywhere else
if popupView != nil
{
popupView!.removeFromSuperview()
popupView = nil
}
// buttons is an array of all normal keyboard buttons
// we use it to check if the button, where the touch ended is the same where the touch began
for button in buttons
{
if (button.frame.contains(point))
{
if (button.button.tag == buttonPressed?.tag)
{
// Still on same button, process the key
delegate?.KeyPressed(key: button.button.title(for: .normal)!) break
}
}
}
}
}
// Let's see if we are moving within the bounds of a popup button
func findPopupButton (point: CGPoint) -> LetterButton?
{
var result : LetterButton? = nil
if (popupView != nil)
{
if (popupView!.frame.contains(point))
{
for sub in popupView!.subviews
{
if (sub.isKind(of: LetterButton.self))
{
let button = sub as! LetterButton
let frame = popupView!.convert(button.frame, to: self)
if (frame.contains(point))
{
button.setIsInside(val: true)
result = button
}
else
{
button.setIsInside(val: false)
}
}
}
}
}
return result
}
【讨论】: