自定义按钮视图:检测触摸向下/向上,快速,并回退拖动到 UIScrollView

Posted

技术标签:

【中文标题】自定义按钮视图:检测触摸向下/向上,快速,并回退拖动到 UIScrollView【英文标题】:Custom button view: detect touch down/up, quickly, and fall back dragging to UIScrollView 【发布时间】:2016-11-21 17:46:46 【问题描述】:

对不起,如果这已经被问到/回答了。

我正在尝试创建一个自定义按钮 (UIView) 来检测触摸向下和向上(以自定义背景颜色、文本颜色等的变化)。

我试过touchesBegan,但反应太慢了。

我已经尝试过UILongPressGestureRecognizer,但它并没有退回到将UIScrollView 拖到下面。我的意思是:拖动按钮时不会拖动滚动视图。

到目前为止,这是我的解决方案,使用 UILongPressGestureRecognizer,想法来自 here。

请问,您能想出比我更好的解决方案吗?谢谢!

/** View that can be pressed like a button */

import UIKit

class ButtonView : UIView, UIGestureRecognizerDelegate 

    static let NoChange =  (btn:ButtonView) in 

    var enabled = true

    /* Called when the view goes to normal state (set desired appearance) */
    var onNormal = NoChange
    /* Called when the view goes to pressed state (set desired appearance) */
    var onPressed = NoChange
    /* Called when the view is released (perform desired action) */
    var action = 


    override init(frame: CGRect)
    
        super.init(frame: frame)

        let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(touched))
        recognizer.delegate = self
        recognizer.minimumPressDuration = 0.0
        addGestureRecognizer(recognizer)
        userInteractionEnabled = true
    

    override func layoutSubviews() 
        super.layoutSubviews()
        onNormal(self)
    

    func touched(sender: UILongPressGestureRecognizer)
    
        guard enabled else  return 

        print("Current state: \(sender.state.rawValue)")

        if sender.state == .Began 
            onPressed(self)
         else if sender.state == .Ended 
            onNormal(self)
            action()
         else if sender.state == .Changed 
            onNormal(self)
            // This cancels recognizer when dragging
            // TODO: but doesn't drag scroll view
            sender.enabled = false
            sender.enabled = true
        
    

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

用法:

    let button = ButtonView()
    // add some views to the ButtonView, e.g. add a label
    button.onNormal =  $0.backgroundColor = normalColor 
    button.onPressed =  $0.backgroundColor = pressedColor 
    button.action =  doSomethingWhenButtonIsClicked() 

【问题讨论】:

GestureRecognizers 可能(根据定义?)需要更长的时间来了解用户在做什么。如果 touchesBegan() 花费的时间太长,您能否以某种方式重新(定义)UI 以消除您正在尝试做的一些事情?换句话说,“你能给我一个你想要的工作示例吗”?你可能想做一些不可能的事情。 谢谢,基本上我有一个“垂直”UIScrollView,最重要的是我有几个视图,包括我的ButtonView。我有其他使用UITapGestureRecognizer 的视图并且它们工作正常(我可以点击打开某些东西,但我也可以在顶部拖动以滚动 UIScrollView)。这些视图实际上位于另一个嵌套的“水平”UIScrollView 中,因此我可以拖动两个方向来滚动主垂直滚动视图或嵌套的水平滚动视图。我会调查是否可以使用UITapGestureRecognizer 解决我的问题。 【参考方案1】:

UIControl 的另一个解决方案。反应比较慢但是还不错,可以在下面拖一个UIScrollView

/** 
 * View that can be pressed like a button.
 * 
 * Note that, since UIView has userInteractionEnabled = true by default,
 * you should disable it for UIView layers that you put insde this ButtonView.
 * Otherwise the ButtonView will not receive the touch events.
 */

// See: http://***.com/q/40726283/custom-button-view

import UIKit

class ButtonView : UIControl 

    static let NoChange =  (btn:ButtonView) in 

    /* Called when the view goes to normal state (set desired appearance) */
    var onNormal = NoChange
    /* Called when the view goes to pressed state (set desired appearance) */
    var onPressed = NoChange
    /* Called when the view goes to disabled state, i.e. enabled = false (set desired appearance) */
    var onDisabled = NoChange
    /* Called when the view is released (perform desired action) */
    var action = 


    override init(frame: CGRect)
    
        super.init(frame: frame)

        self.addTarget(self, action: #selector(pressed), forControlEvents: .TouchDown)
        self.addTarget(self, action: #selector(clicked), forControlEvents: .TouchUpInside)
        self.addTarget(self, action: #selector(cancelled), forControlEvents: [.TouchCancel, .TouchDragExit, .TouchUpOutside])

        userInteractionEnabled = true // this is the default!
    

    override func layoutSubviews() 
        super.layoutSubviews()
        updateBaseStyle()
    

    func pressed() 
        onPressed(self)
    

    func clicked() 
        onNormal(self)
        action()
    

    func cancelled() 
        onNormal(self)
    

    override var enabled: Bool 
        willSet(e) 
            super.enabled = e
            updateBaseStyle()
        
    

    func updateBaseStyle() 
        if self.enabled 
            onNormal(self)
         else 
            onDisabled(self)
        
    

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

用法类似(我只是加了onDisable):

let button = ButtonView()
// add some views to the ButtonView, e.g. add a label
// note child views should have userInteractionEnabled = false
button.onNormal =  $0.backgroundColor = normalColor 
button.onPressed =  $0.backgroundColor = pressedColor 
button.onDisabled =  $0.backgroundColor = disabledColor 
button.action =  callToAction() 

【讨论】:

以上是关于自定义按钮视图:检测触摸向下/向上,快速,并回退拖动到 UIScrollView的主要内容,如果未能解决你的问题,请参考以下文章

在 UIScrollView 中检测“向上”UIGesture

如何在快速向上和向下滚动表格时停止自动重新加载表格视图? [关闭]

在 ios 的滚动视图内检测按钮的触摸

iOS 上滑菜单

如何检测 UITableView 上的“快速触摸”以切换到/从全屏?

c_cpp 方形按钮与背景动画触摸向下和向上(突出显示)