添加了 UITableViewCell GestureRecognizer 委托不工作

Posted

技术标签:

【中文标题】添加了 UITableViewCell GestureRecognizer 委托不工作【英文标题】:Added UITableViewCell GestureRecognizer Delegate Not Working 【发布时间】:2015-08-20 20:40:06 【问题描述】:

当前进度

我有一个表格,滑动手势应该会调出一个新的 ViewController。它确实有效,可以调用 segue 并加载新的 VC(下面的所有代码,应该对任何只想添加手势的人都有帮助)。

问题

但我想将滑动单元格的索引值传递给新的 ViewController,但我无法执行此操作或复制我找到的指南中解释的方法。

我的表格使用自定义单元格类,这是添加手势的地方。添加了手势,我已经测试过了,它确实使用委托来触发主 VC 中的一个函数并触发 segue。

当我尝试捕获初始发件人并将其传递给委托时,该错误肯定似乎在一开始就发生了。

代码

这是我的自定义单元格的代码:

class CustomTableViewCell: UITableViewCell 

@IBOutlet var nameLabel: UILabel!
@IBOutlet var descLabel: UILabel!

var delegate: mainViewDelegate!

override func awakeFromNib() 
    super.awakeFromNib()
    // Initialization code

    //Create 'swipeLeft' variable, provide action (swipedLeft) and add to cell
    let swipeLeft: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "swipedLeft")
    swipeLeft.direction = UISwipeGestureRecognizerDirection.Left
    self.addGestureRecognizer(swipeLeft)



override func setSelected(selected: Bool, animated: Bool) 
    super.setSelected(selected, animated: animated)
    // Configure the view for the selected state


func swipedLeft (sender: UISwipeGestureRecognizer) 
    println("swipe detected, cell function run")
    if(self.delegate != nil)
        self.delegate.cellSwipedLeft(sender)
    
   

协议:

protocol mainViewDelegate 
    func cellSwipedLeft (UISwipeGestureRecognizer)

主 ViewController 标头:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, mainViewDelegate 

我还在主 VC 的 cellForRowAtIndexPath 函数中添加了所需的委托行:

func tableView... 
...  (edited out to save space)
cell.delegate = self

主要的VC函数:

func cellSwipedLeft (sender: UISwipeGestureRecognizer) 
    println("cellSwipedLeft main VC func ran")
    performSegueWithIdentifier("modalTo_HomeTaskAction", sender: nil)

现在,如果我没有在参数中传递任何内容,这一切都可以正常工作,但是当我添加 UISwipeGestureRecognizer 时,它会失败并出现 Thread 1: signal SIGABRT 错误。我的目标是成功传递手势,然后我将添加以下代码以获取索引并使用 prepareForSegue 将其传递给我的 VC:

let gesture = sender as! UISwipeGestureRecognizer
let cell = gesture.view! as! CustomTableViewCell_F2
let indexPath = tableView.indexPathForCell(cell)

所以最大的问题是,为什么传递 UISwipeGestureRecognizer 会给我一个这样开始的错误:

2015-08-21 03:23:39.566 AppName[10170:945334] -[AppName.CustomTableViewCell swipedLeft]: unrecognized selector sent to instance 0x7fb149766560
2015-08-21 03:23:39.619 AppName[10170:945334] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-   [AppName.CustomTableViewCell swipedLeft]: unrecognized selector sent to instance 0x7fb149766560'
*** First throw call stack:
(
    0   CoreFoundation                          0x0000000106dfcc65 __exceptionPreprocess + 165
1   libobjc.A.dylib                     0x0000000108ba9bb7 

【问题讨论】:

【参考方案1】:

Dave,这是一种更简单的方法,它没有协议,而是使用块。在您的自定义 UITableViewCell 中,我们这样做:

设置:

import Foundation
import UIKit

class EXTableViewCell: UITableViewCell 
    @IBOutlet var nameLabel: UILabel!
    @IBOutlet var descLabel: UILabel!
    var doWork: (() -> Void)?
    func swipedLeft (sender: UISwipeGestureRecognizer) 
        if let callback = self.doWork 
            println("swipe detected, cell function run")
            callback ()
        
    
    override func awakeFromNib() 
        super.awakeFromNib()
        let swipeLeft: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "swipedLeft")
        swipeLeft.direction = UISwipeGestureRecognizerDirection.Left
        self.addGestureRecognizer(swipeLeft)
    
    override func setSelected(selected: Bool, animated: Bool) 
        super.setSelected(selected, animated: animated)
    

自定义视图控制器:

import UIKit

class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate 
    override func viewWillAppear(animated: Bool) 
        super.viewWillAppear(animated)
    
    override func viewDidLoad() 
        super.viewDidLoad()
    
    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
    
    func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return  0 //replace with the correct info
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 4 //replace with the correct info
    

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! FFViewCell
          cell.doWork = 
              () -> Void in
              self.doStuff(indexPath.row)
        
          cell.labelMessage.text = items[indexPath.row] as String
          return cell
      
      func doStuff(integer: NSInteger) 
          println("i got here \(integer)")
      

这是如何工作的:

您看,我们正在声明一个块属性,它允许我们将一个空的“函数”调用 (PER SE) 传递给您在 UIViewController 中创建的任何“EXTableViewCell”。

所以,在自定义的 UITableViewCell 中,我们声明了一个 void 块属性:

var doWork: (() -> Void)?

我们将一个触摸处理程序附加到单元格:

func swipedLeft (sender: UISwipeGestureRecognizer) 
        if let callback = self.doWork 
            println("swipe detected, cell function run")
            callback ()
        
    

然后我们在 UIViewController 内部或主 UIViewController 中调用此处理程序,并在配置表格视图单元格时设置此属性:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! EXTableViewCell
        cell.doWork = 
            () -> Void in
            self.doStuff()
        
        return cell
    

具体来说:

cell.doWork = 
            () -> Void in
            self.doStuff()
        

显然,我们随后设置了“doStuff”函数来执行我们希望它在 UIViewController 中执行的操作:

func doStuff() 
        println("i got here")
    

没有协议,没有混乱,没有让委托发生,所有基于块的功能。我没有用实际的 UIViewController 测试过这段代码,但是,这在 Objective-C 中可以完美运行,在发布这段代码之前,我确保它可以编译。

快速说明块的魅力,几乎所有看起来超级复杂的委托和协议都可以用块来完成,困难的部分是习惯使用块并了解它们的多功能性。可能最令人愉快的部分是您可以像使用普通属性一样使用“块属性”,但还有将处理程序事件附加到拥有块属性的对象的额外好处。无论如何,您可能还需要做一件事,但这很简单:

您可能需要像这样启动自定义表格视图单元格,使其成为 UIGestureRecognizer 的代表,如下所示:

class EXTableViewCell: UITableViewCell, UIGestureRecognizerDelegate 

您可能需要在自定义表格视图单元类中声明您的手势识别器,使其看起来像这样:

swipeLeft.delegate = self
swipeLeft.cancelsTouchesInView = false

另外,如果您在实现这一点时遇到问题,请告诉我,我会看看我是否不能完全实现它。

工作示例,经过测试并准备就绪:

自定义tableViewCell:

import Foundation
import UIKit

class FFViewCell: UITableViewCell, UIGestureRecognizerDelegate 

    var labelMessage = UILabel()

    var doWork: (() -> Void)?

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        let swipeLeft: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "this")

        swipeLeft.delegate = self
        swipeLeft.cancelsTouchesInView = false

        self.addGestureRecognizer(swipeLeft)

        labelMessage.setTranslatesAutoresizingMaskIntoConstraints(false)
        contentView.addSubview(labelMessage)
        var viewsDict =  ["labelMessage" : labelMessage]

        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[labelMessage]|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[labelMessage]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))

    
    required init(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    
    func this () 
        if let callback = self.doWork 
            println("swipe detected, cell function run")
            callback ()
        
    


AppDelegate:

import UIKit

@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate 

    var window: UIWindow?
    var rootViewController: UINavigationController?
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool 
        window = UIWindow(frame: UIScreen.mainScreen().bounds)

        rootViewController = UINavigationController(rootViewController: ViewController())
        if let window = window 
            window.backgroundColor = UIColor.whiteColor()

            window.rootViewController = rootViewController

            window.makeKeyAndVisible()
        
        return true
    
    func applicationWillResignActive(application: UIApplication) 
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    

    func applicationDidEnterBackground(application: UIApplication) 
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    

    func applicationWillEnterForeground(application: UIApplication) 
        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    

    func applicationDidBecomeActive(application: UIApplication) 
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    

    func applicationWillTerminate(application: UIApplication) 
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    



视图控制器:

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate 

    var tableView : UITableView?
    var items = ["asdf","asdf","asdf","asdf","asdf"]

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) 
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    
    required init(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    
    override func viewDidLoad() 
        super.viewDidLoad()
        tableView = UITableView(frame: CGRectMake(0, 0, 414, 736), style: UITableViewStyle.Plain)
        tableView!.delegate = self
        tableView!.dataSource = self
        tableView!.registerClass(FFViewCell.self, forCellReuseIdentifier: "Cell")
        self.view .addSubview(tableView!)
    
    override func loadView() 
        var stuf = UIView()
        stuf.frame = CGRectMake(0, 0, 414, 736)
        stuf.backgroundColor = UIColor .redColor()
        self.view = stuf
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return items.count;
    
    func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return 1
    
    func tableView(tableView:UITableView, heightForRowAtIndexPath indexPath:NSIndexPath)->CGFloat
    
        return 44
       
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! FFViewCell
        cell.doWork = 
            () -> Void in
            self.doStuff()
        
        cell.labelMessage.text = items[indexPath.row] as String
        return cell
    
    func doStuff() 
        println("i got here")
    

这是“滑动手势”代码,Dave:

import Foundation
import UIKit

class FFViewCell: UITableViewCell, UIGestureRecognizerDelegate 

    var labelMessage = UILabel()

    var doWork: (() -> Void)?

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        let swipeLeft: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "this")
        swipeLeft.direction = UISwipeGestureRecognizerDirection.Left
        swipeLeft.delegate = self
        swipeLeft.cancelsTouchesInView = false
        self.addGestureRecognizer(swipeLeft)

        labelMessage.setTranslatesAutoresizingMaskIntoConstraints(false)
        contentView.addSubview(labelMessage)
        var viewsDict =  ["labelMessage" : labelMessage]

        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[labelMessage]|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[labelMessage]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))

    
    required init(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    
    func this () 
        if let callback = self.doWork 
            println("swipe detected, cell function run")
            callback ()
        
    


【讨论】:

拉塞拉克斯,谢谢。这块东西对我来说是全新的。空虚是什么意思?另外,我现在是否仅通过引用“单元格”变量来获取索引路径?我一定会尝试这个,但我想更加清醒。现在快早上 6 点了,我整晚都在做这个,所以大约 8 到 10 个小时后,我会告诉你它是否有效。谢谢 是的,所以你可以像你一样引用单元格,很有可能,并且“void”只是意味着没有返回值,它只是起作用的函数,就是这样 我可能会在接下来的 2 小时内完成一个完整的解决方案,所以当你起床并醒来时重新检查答案 其实,Dave,我已经完成了,它正在工作,我会添加详细信息。 没用!让我大吃一惊的是,我删除了所有与协议相关的代码,三次检查我做了你概述的所有事情,错误是一样的。在控制台中打印的是:'AppName.CustomTableViewCell swipedLeft]: unrecognized selector sent to instance 0x7fa893cc1a50' 作为提醒,这很奇怪,因为手势确实适用于协议,但只有当我从参数中删除 UISwipeGestureRecognizer 时被委托的功能。现在,由于该委托已经消失并且您的代码在其中并且它得到了同样的错误?这令人费解

以上是关于添加了 UITableViewCell GestureRecognizer 委托不工作的主要内容,如果未能解决你的问题,请参考以下文章

如何捕获 UIButton 事件:在自定义 UITableViewCell 实现中添加了 UIButton

将大于 cellHeight 的子视图添加到 UITableViewCell?

如何向 UITableViewCell 添加手势?

UITableViewCell 无法添加子视图

在 UITableviewCell 中添加 uiview

在 UITableViewCell 中添加渐变隐藏 UILabel 文本