简体   繁体   English

Swift:如何将NSIndexPath绑定到addTarget

[英]Swift: How can I bind an NSIndexPath to a addTarget

I have a UISwitch in each of my dynamically created rows of which I want to bind an NSIndexPath to an addTarget selector. 我在每个动态创建的行中都有一个UISwitch ,我想将NSIndexPath绑定到addTarget选择器。

I had considered allowing the user to tap the row to toggle the switch, but from a UX perspective it makes more sense to have the switch handle this method, therefore using didSelectRowAtIndexPath is not an appropriate solution. 我曾考虑过允许用户点击行来切换开关,但是从UX的角度来看,让开关来处理此方法更有意义,因此使用didSelectRowAtIndexPath是不合适的解决方案。

When my cell is created I currently bind a selector like this: 创建单元格时,我当前绑定一个选择器,如下所示:

// Create listener for each switch
prefCell.subscribed?.addTarget(self, 
                               action: "switchFlipped:",
                               forControlEvents: UIControlEvents.ValueChanged)

Which calls the corresponding method: 其中调用了相应的方法:

func switchFlipped(flipSwitch: UISwitch, indexPath: NSIndexPath) {}

Obviously this throws an error because NSIndexPath isn't a valid reference, as I believe it will only send the buttons reference. 显然,这会引发错误,因为NSIndexPath不是有效的引用,因为我认为它只会发送按钮引用。 Is there any way I can bind the index path? 有什么办法可以绑定索引路径? If not, is there any way to get the current cell from the context of the UISwitch ? 如果不是,是否有任何方法可以从UISwitch的上下文中获取当前单元格?

The target-action pattern does not allow arbitrary selectors. 目标动作模式不允许使用任意选择器。 You can have f(sender:) and f(sender:event:) . 您可以有f(sender:)f(sender:event:) Both won't help you. 两者都帮不了你。 But you can use code to figure out the indexPath in the function. 但是您可以使用代码找出函数中的indexPath。

Code should be self explanatory: 代码应具有自我解释性:

func switchFlipped(flipSwitch: UISwitch) {
    let switchOriginInTableView = flipSwitch.convertPoint(CGPointZero, toView: tableView)
    if let indexPath = tableView.indexPathForRowAtPoint(switchOriginInTableView) {
        println("flipped switched at \(indexPath)")
    }
    else {
        // this should not happen
    }
}

You can try 3 approaches, as you see fit. 您可以尝试3种合适的方法。

  1. Create a custom UISwitch class and add an NSIndexPath property to it. 创建一个自定义UISwitch类,并向其添加NSIndexPath属性。 When you receive notification, type cast to your custom class and access the NSIndexPath. 收到通知时,将强制类型转换为自定义类并访问NSIndexPath。
  2. Create a custom UITableViewCell class and save NSIndexPath to it. 创建一个自定义UITableViewCell类并将NSIndexPath保存到该类。 When you get notified of UISwitch, get its superviews and see which one is your customUITableViewCell instance and get the property. 当您收到UISwitch的通知时,获取其超级视图,并查看哪个是您的customUITableViewCell实例并获取该属性。
  3. If you only have one section, it means all you need to worry about are rows. 如果只有一个部分,则意味着您只需要担心行。 Set the UISwitch tag as the row number and access it when switch is flipped. 将UISwitch标记设置为行号,并在切换开关时访问它。
  4. The Most Tech-Savy Solution Use Extensions of Swift. 技术含量最高的解决方案使用Swift的扩展。

Extensions add new functionality to an existing class, structure, or enumeration type. 扩展将新功能添加到现有的类,结构或枚举类型。 This includes the ability to extend types for which you do not have access to the original source code Apple Docs 这包括扩展您无法访问原始源代码Apple Docs的类型的能力

This should be the a good solution, not depending on a point. 这应该是一个很好的解决方案,而不是依赖于一点。 You call the superviews of the Switch and therefore calculate the index path of the cell. 您调用Switch的超级视图,并因此计算单元的索引路径。

func switchTapped(sender: UISwitch) -> NSIndexPath {
    let cell = sender.superview!.superview! as UITableViewCell
    return tableView.indexPathForCell(cell)!
}

This reliably works, that's why you can simply unwrap any optionals. 这可靠地起作用,这就是为什么您可以简单地展开任何可选项的原因。

Use delegation. 使用委托。

Your switch's target action should be a method in your custom UITableViewCell. 开关的目标操作应该是自定义UITableViewCell中的方法。 That custom tableviewcell should declare a protocol and the tableview should be it's delegate. 该自定义tableviewcell应该声明一个协议,而tableview应该是它的委托。 Then your tableviewcell should call its delegate method from within the switch's target function. 然后,您的tableviewcell应该从开关的target函数中调用其委托方法。

The delegate method for your tableviewcell should have a parameter in which the cell passes self . tableviewcell的委托方法应具有一个参数,单元格将在其中传递self Then in your tableviewcontroller delegate implementation you can use indexPathForCell: 然后在tableviewcontroller委托实现中,可以使用indexPathForCell:

UPDATE: 更新:

Here is the apple doc for swift protocols and delegates: 这是快速协议和委托的苹果文档:

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html

Simply put, you need to define a protocol 简而言之,您需要定义一个协议

protocol CustomUITableViewCellDelegate {
    func tableViewCellSubscribed(cell: myCustomUITableViewCell) -> Void
}

then you make your tableviewcontroller class conform to that protocol like this: 然后使您的tableviewcontroller类符合该协议,如下所示:

class myTableViewController: UITableViewController, CustomUITableViewCellDelegate {

func tableViewCellSubscribed(cell: myCustomUITableViewCell) -> Void {
    //this is where you handle whatever operations you want to do regarding the switch being valuechanged
}

// class definition goes here
}

Then your custom UITableViewCell class should have a delegate property like this: 然后,您的自定义UITableViewCell类应具有如下的委托属性:

class myCustomUITableViewCell : UITableViewCell {
    var delegate : CustomUITableViewCellDelegate?

    //class definition goes here
}

Finally, set the cell's delegate in tableView:cellForRowAtIndexPath: and you're good to go. 最后,在tableView:cellForRowAtIndexPath:设置单元格的委托,您就可以开始了。 You just need to call your tableViewCellSubscribed: delegate function from within the target method of your switch action. 您只需要从switch操作的target方法中调用tableViewCellSubscribed:委托函数。

I've used this approach in many projects, you can just add a callback to your custom cell, each time your switch changes, you can run this callback and catch it inside your table datasource implementation, below a dirt and simple example. 我在许多项目中都使用过这种方法,您只需在自定义单元格中添加一个回调,每次开关更改时,您都可以运行此回调并将其捕获到表数据源实现中,下面是一个简单的示例。

1. Create a UITableViewCell custom class 1.创建一个UITableViewCell自定义类

// An example of custom UITableViewCell class

class CustomCell:UITableViewCell {

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

  var onSwitchChange:() -> Void?

}

class TableDataSource:UITableViewDataSource {

  func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 0
  }

  func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell:CustomCell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as CustomCell
    cell.onSwitchChange = {
      NSLog("Table: \(tableView) Cell:\(cell) Index Path: \(indexPath)")
    }
  }
}

Acctually,Apple has already provide API to get the indexPath. 准确地说,苹果已经提供了获取索引路径的API。 Simply like this (in Objective-C) 就像这样(在Objective-C中)

NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];

Far easier, you can use the tag variable for this : 您可以轻松地使用tag变量:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    ...
    cell.mySwitch.tag = indexPath.item
    cell.mySwitch.addTarget(self, action: #selector(switchChanged), for: UIControlEvents.valueChanged)
    ...
}

func switchChanged(mySwitch: UISwitch) {
    let indexPathItem = mySwitch.tag
    ...
}

From official doc : 来自官方文档:

var tag: Int { get set } var标记:Int {获取设置}

An integer that you can use to identify view objects in your application. 可用于标识应用程序中的视图对象的整数。

The default value is 0. You can set the value of this tag and use that value to identify the view later. 默认值为0。您可以设置此标记的值,并在以后使用该值标识视图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在Swift中将NSIndexPath转换为Int? - How convert NSIndexPath to Int in Swift? 如何快速将 NSIndexPath 添加到 NSUserDefaults - How to add NSIndexPath to NSUserDefaults in swift 如何在 swift 3 中使用 addTarget 方法 - How to use addTarget method in swift 3 如何判断 UITableView 是否包含特定的 NSIndexPath? - How can I tell if a UITableView contains a specific NSIndexPath? 如何在addTarget的操作中传递参数? - How can I pass a parameter in addTarget's action? 如何仅对UIControlEvent.allEditingEvents覆盖UITextField中的addTarget? - How can I override addTarget in a UITextField for only UIControlEvent.allEditingEvents? 我如何在目标C中覆盖UIButton的addTarget方法 - How I can override addTarget method of UIButton in objective c 如何在另一个UIViewController中快速更改NSIndexPath - How to change NSIndexPath in another UIViewController in swift 如何在 swift 5 的 UIButton 的 addTarget 中添加带参数的函数? - How to add a function with parameter in addTarget of UIButton in swift 5? 在Swift中的普通类中以编程方式创建按钮时,如何在Button类的addTarget方法中调用方法 - How invoke a method in the method addTarget of the Button class when I create programmatically the button in a plain class in Swift
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM