Swift - 如何将点击手势添加到 UIViews 数组?

Posted

技术标签:

【中文标题】Swift - 如何将点击手势添加到 UIViews 数组?【英文标题】:Swift - How to add tap gesture to array of UIViews? 【发布时间】:2019-01-21 14:57:15 【问题描述】:

希望将点击手势添加到 UIViews 数组中 - 没有成功。在此阶段似乎无法识别 Tap。

在下面的代码(摘录)中: 在主视图上显示一系列PlayingCardViews(每个UIView)。 集合为一个数组:cardView。 需要能够独立点击每个PlayingCardView(然后能够识别哪个被点击了)。

@IBOutlet private var cardView: [PlayingCardView]!


override func viewDidLoad() 
    super.viewDidLoad()
    let tap = UITapGestureRecognizer(target: self, action: #selector(tapCard(sender: )))
    for index in cardView.indices 
        cardView[index].isUserInteractionEnabled = true
        cardView[index].addGestureRecognizer(tap)
        cardView[index].tag = index
    


@objc func tapCard (sender: UITapGestureRecognizer) 
    if sender.state == .ended 
        let cardNumber = sender.view.tag
        print("View tapped !")
    

【问题讨论】:

那么tapCard 函数是否被调用了?顺便说一句,您还没有使用 cardNumber 属性。 正确@DávidPásztor:'tapCard' 函数未被调用(即,当我在 IB 上点击任何 PlayingCardView 时,控制台上没有打印任何内容。关于 'cardNumber',您也是正确的,尚未使用但打算以后再说。 你不能确定函数没有被print 语句没有被执行,因为你将它嵌套在if 语句中:) 你应该使用调试器来检查函数实际上是否被调用(或在if 语句之外放置一个打印语句),但if 语句似乎是不必要的。 删除了 if 语句,只保留了函数中的 print。仍然没有打印到控制台。 您当前案例中的点击将仅添加到最后一个视图中 【参考方案1】:

你需要

@objc func tapCard (sender: UITapGestureRecognizer)  
     let clickedView = cardView[sender.view!.tag] 
     print("View tapped !" , clickedView ) 

这里不需要检查状态,因为这种手势类型的方法只调用一次,而且每个视图都应该有一个单独的点击,所以在for循环中创建它

for index in cardView.indices   
   let tap = UITapGestureRecognizer(target: self, action: #selector(tapCard(sender: )))

【讨论】:

谢谢@sh_khan。只要我可以通过点击手势调用“tapCard”函数,就会尝试这样做。 谢谢@sh_khan。这似乎确实有效......在这里测试并将很快确认。另外,顺便说一句:在哪里有 for 循环创建点击手势的最佳位置:在 viewDidLoad (如上)或 cardView 声明中的 didSet 中?这有什么不同吗 ?一个更合适吗? (很快恢复以确认我是否能够按照您的解决方案进行修复) 两种情况都可以,但我推荐viewDidLoad 经过测试,似乎有效,@sh_khan。谢谢!【参考方案2】:

我不会推荐所选答案。因为在循环中创建一个 tapGesture 数组对我来说没有意义。最好在 PlaycardView 中添加手势。

相反,应使用 UICollectionView 设计此类布局。如果您需要自定义布局并且想要使用滚动视图甚至 UIView,那么更好的方法是创建单个手势识别器并添加到超级视图。

使用点击手势,您可以获得点击的位置,然后您可以使用该位置获取 selectedView。

请参考以下示例:

import UIKit

class PlayCardView: UIView 

    override init(frame: CGRect) 
        super.init(frame: frame)
        backgroundColor = UIColor.red
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        backgroundColor = UIColor.red
    



class SingleTapGestureForMultiView: UIViewController 

    var viewArray: [UIView]!
    var scrollView: UIScrollView!

    override func viewDidLoad() 
        super.viewDidLoad()
        scrollView = UIScrollView(frame: UIScreen.main.bounds)
        view.addSubview(scrollView)

        let tapGesture = UITapGestureRecognizer(target: self,
                                                action: #selector(tapGetsure(_:)))
        scrollView.addGestureRecognizer(tapGesture)
        addSubviews()
    

    func addSubviews() 

        var subView: PlayCardView
        let width = UIScreen.main.bounds.width;
        let height = UIScreen.main.bounds.height;
        let spacing: CGFloat = 8.0
        let noOfViewsInARow = 3
        let viewWidth = (width - (CGFloat(noOfViewsInARow+1) * spacing))/CGFloat(noOfViewsInARow)
        let viewHeight = (height - (CGFloat(noOfViewsInARow+1) * spacing))/CGFloat(noOfViewsInARow)

        var yCordinate =  spacing
        var xCordinate =  spacing

        for index in 0..<20 

            subView = PlayCardView(frame: CGRect(x: xCordinate, y: yCordinate, width: viewWidth, height: viewHeight))
            subView.tag = index
            xCordinate += viewWidth + spacing
            if xCordinate > width 
                xCordinate = spacing
                yCordinate += viewHeight + spacing
            

            scrollView.addSubview(subView)
        
        scrollView.contentSize = CGSize(width: width, height: yCordinate)
    

    @objc
    func tapGetsure(_ gesture: UITapGestureRecognizer) 

        let location = gesture.location(in: scrollView)
        print("location = \(location)")

        var locationInView = CGPoint.zero

        let subViews = scrollView.subviews
        for subView in subViews 
            //check if it subclass of PlayCardView
            locationInView = subView.convert(location, from: scrollView)

            if subView.isKind(of: PlayCardView.self) 
                if subView.point(inside: locationInView, with: nil) 
                    // this view contains that point
                    print("Subview at \(subView.tag) tapped");
                    break;
                
            

        

    


【讨论】:

感谢@ankur 提供替代解决方案。【参考方案3】:

您可以尝试将视图控制器作为参数传递给视图,以便它们可以从视图调用父视图控制器上的函数。要减少内存,您可以使用协议。例如

    protocol testViewControllerDelegate: class 
        func viewTapped(view: UIView)
    
    
    class testClass: testViewControllerDelegate 
    
    @IBOutlet private var cardView: [PlayingCardView]!
    
    
    override func viewDidLoad() 
        super.viewDidLoad()
        for cardView in self.cardView 
            cardView.fatherVC = self
        
        
        
        
        func viewTapped(view: UIView) 
            // the view that tapped is passed ass parameter
        
    
    
    
    
    
    class PlayingCardView: UIView 
        
        weak var fatherVC: testViewControllerDelegate?
        override func awakeFromNib() 
            super.awakeFromNib()
            let gr = UITapGestureRecognizer(target: self, action: #selector(self.viewDidTap))
self.addGestureRecognizer(gr)
        
        
        @objc func viewDidTap() 
            fatherVC?.viewTapped(view: self)
        
    

【讨论】:

谢谢,去看看。还测试了来自@sh_khan 的解决方案,似乎有效。

以上是关于Swift - 如何将点击手势添加到 UIViews 数组?的主要内容,如果未能解决你的问题,请参考以下文章

手势识别器不起作用。执行下方元素的操作 - SWIFT

如何将手势识别器添加到collectionview单元格中的uiview

在同一个 UIView 中多次点击手势检测

如何以编程方式触发 UIView 的点击手势识别器

在 Swift 上将所有手势从 UIView 转换为 UIScrollView

以编程方式将点击手势添加到 UILabel