添加了 UITableViewCell GestureRecognizer 委托不工作

Added UITableViewCell GestureRecognizer Delegate Not Working

当前进度

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

问题

但我想将新的 ViewController 传递给 swiped-cell 的索引值,但我无法做到这一点,也无法复制我找到的指南中解释的方法。

我的 table 使用自定义单元格 class,这是添加手势的地方。添加了手势,我已经对其进行了测试,它确实使用委托来触发 main 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)
}

主要ViewControllerheader:

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 时,它失败并出现线程 1:信号 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 

戴夫,这是一种更简单的方法,它没有协议,而是使用块。在您的自定义 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)
    }
}

自定义 ViewController:

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)")
      }
}

这是如何工作的:

你看,我们正在声明一个块 属性,它允许我们将一个空的 "function" 调用 (PER SE) 传递给您在 UI[=77] 中创建的任何 "EXTableViewCell" =].

因此,在自定义 UITableViewCell 中,我们声明了一个空块 属性:

var doWork: (() -> Void)?

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

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

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

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" 函数来执行我们希望它在我们的 UI 中执行的操作ViewController:

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

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

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

您可能需要像这样启动自定义 table 视图单元格,使其成为 UIGestureRecognizer 的委托:

class EXTableViewCell: UITableViewCell, UIGestureRecognizerDelegate {

并且您可能需要在自定义 table 视图单元格 class 中声明您的手势识别器,因此它看起来像这样:

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:.
    }


}

ViewController:

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")
    }
}

这是 "swipe gesture" 代码,戴夫:

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 ()
        }
    }

}