Đính kèm tham số vào hành động button.addTarget trong Swift


102

Tôi đang cố gắng truyền một tham số bổ sung cho hành động buttonClicked, nhưng không thể tìm ra cú pháp nên là gì trong Swift.

button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)

Bất kỳ phương pháp nhấp vào nút nào của tôi:

func buttonClicked(sender:UIButton)
{
    println("hello")
}

Bất cứ ai có bất kỳ ý tưởng?

Cảm ơn bạn đã giúp đỡ.


1
Tham khảo liên kết này để có câu trả lời chi tiết và phương pháp thực hành tốt nhất
sheetal

Câu hỏi không có câu trả lời tốt vì nó đã mô tả một kiến ​​trúc có vấn đề. Bạn không cần thêm một tham số. Việc cần một tham số bổ sung là một vấn đề trong kiến ​​trúc của bạn.
Sulthan

WARNING TO INTERNET: please check out my answer at the bottom
Mr Heelis

Câu trả lời:


170

Bạn không thể chuyển các thông số tùy chỉnh trong addTarget:. Một lựa chọn thay thế được đặt thuộc tagtính của nút và hoạt động dựa trên thẻ.

button.tag = 5
button.addTarget(self, action: "buttonClicked:", 
    forControlEvents: UIControlEvents.TouchUpInside)

Hoặc đối với Swift 2.2 trở lên :

button.tag = 5
button.addTarget(self,action:#selector(buttonClicked),
    forControlEvents:.TouchUpInside)

Bây giờ làm logic dựa trên thuộc tagtính

@objc func buttonClicked(sender:UIButton)
{
    if(sender.tag == 5){

        var abc = "argOne" //Do something for tag 5
    }
    print("hello")
}

4
Xin chào, Nếu tôi muốn lấy indexPath hoặc ô của nút trong tableView, có được không?
NKurapati

1
Đây là một gợi ý: Không đặt phương pháp ở chế độ riêng tư.
OhadM

2
Có vẻ như loại chỉ có thể là một Int. Không có gì khác.
Fearguy

2
điều gì xảy ra nếu tôi muốn chuyển hàng và phần cùng nhau?
Yestay Muratov

3
chỉ để bạn biết, mặc dù 118 upvotes (và đếm) đây là một cách sai lầm để làm điều này
ông Heelis

79

Nếu bạn muốn gửi các tham số bổ sung cho phương thức buttonClicked, ví dụ: indexPath hoặc urlString, bạn có thể phân lớp UIButton:

class SubclassedUIButton: UIButton {
    var indexPath: Int?
    var urlString: String?
}

Đảm bảo thay đổi lớp của nút trong trình kiểm tra danh tính thành lớp conUIButton. Bạn có thể truy cập các tham số bên trong phương thức buttonClicked bằng cách sử dụng sender.indexPathhoặc sender.urlString.

Lưu ý: Nếu nút của bạn nằm bên trong ô, bạn có thể đặt giá trị của các tham số bổ sung này trong phương thức cellForRowAtIndexPath (nơi nút được tạo).


5
Đây là cách duy nhất phù hợp với tôi. Nhưng tôi rất tò mò, liệu đây có phải là sự phản mẫu hay không?
Fearguy

1
Đây là cách tốt nhất, tôi nghĩ rằng
Duy Hoàng

29

Tôi đánh giá cao mọi người nói rằng sử dụng thẻ, nhưng thực sự bạn cần phải mở rộng lớp UIButton và chỉ cần thêm đối tượng vào đó ..

Thẻ là một cách vô vọng để giải quyết vấn đề này. Mở rộng UIButton như thế này (trong Swift 4)

import UIKit
class PassableUIButton: UIButton{
    var params: Dictionary<String, Any>
    override init(frame: CGRect) {
        self.params = [:]
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        self.params = [:]
        super.init(coder: aDecoder)
    }
}

thì cuộc gọi của bạn có thể là cuộc gọi (LƯU Ý dấu hai chấm ":" in Selector(("webButtonTouched:")))

let webButton = PassableUIButton(frame: CGRect(x:310, y:40, width:40, height:40))
webButton.setTitle("Visit",for: .normal)
webButton.addTarget(self, action: #selector(YourViewController.webButtonTouched(_:)), for:.touchUpInside)
webButton.params["myvalue"] = "bob"

rồi cuối cùng nắm bắt tất cả ở đây

@IBAction func webButtonTouched(_ sender: PassableUIButton) {
    print(sender.params["myvalue"] ?? "")
}

Bạn làm điều này một lần và sử dụng nó trong suốt dự án của mình (bạn thậm chí có thể làm cho lớp con có một "đối tượng" chung và đặt bất cứ thứ gì bạn thích vào nút!). Hoặc sử dụng ví dụ trên để đặt một số lượng vô tận các tham số khóa / chuỗi vào nút. Thực sự hữu ích để bao gồm những thứ như url, phương pháp xác nhận thông báo, v.v.

Ngoài ra, điều quan trọng là SOcộng đồng phải nhận ra rằng có cả một thế hệ hành vi xấu đang bị cắt giảm thị hiếu trên internet bởi một số lượng đáng báo động các lập trình viên không hiểu / chưa được dạy / bỏ sót điểm khái niệm củaobject extensions


10

Đối với Swift 3.0, bạn có thể sử dụng sau

button.addTarget(self, action: #selector(YourViewController.YourMethodName(_:)), for:.touchUpInside)

func YourMethodName(_ sender : UIButton) {
    print(sender.tag)

}

9

Swift 4.2

Kết quả:

testButton.on(.touchUpInside) { (sender, event) in
    // You can use any reference initialized before the code block here
    // You can access self by adding [weak self] before (sender, event)
    // You can then either make self strong by using a guard statement or use a optional operator (?)
    print("user did press test button")
}

Trong tệp, UIButton+Events.swifttôi đã tạo một phương thức mở rộng để UIButtonliên kết a UIControl.Eventvới một trình xử lý hoàn thành được gọi là EventHandler:

import UIKit

fileprivate var bindedEvents: [UIButton:EventBinder] = [:]

fileprivate class EventBinder {

    let event: UIControl.Event
    let button: UIButton
    let handler: UIButton.EventHandler
    let selector: Selector

    required init(
        _ event: UIControl.Event,
        on button: UIButton,
        withHandler handler: @escaping UIButton.EventHandler
    ) {
        self.event = event
        self.button = button
        self.handler = handler
        self.selector = #selector(performEvent(on:ofType:))
        button.addTarget(self, action: self.selector, for: event)
    }

    deinit {
        button.removeTarget(self, action: selector, for: event)
        if let index = bindedEvents.index(forKey: button) {
            bindedEvents.remove(at: index)
        }
    }
}

private extension EventBinder {

    @objc func performEvent(on sender: UIButton, ofType event: UIControl.Event) {
        handler(sender, event)
    }
}

extension UIButton {

    typealias EventHandler = (UIButton, UIControl.Event) -> Void

    func on(_ event: UIControl.Event, handler: @escaping EventHandler) {
        bindedEvents[self] = EventBinder(event, on: self, withHandler: handler)
    }
}

Lý do tại sao tôi sử dụng một lớp tùy chỉnh để ràng buộc sự kiện là để có thể loại bỏ tham chiếu sau này khi nút được deintialised. Điều này sẽ ngăn ngừa rò rỉ bộ nhớ có thể xảy ra. Điều này không thể thực hiện được trong UIButtonphần mở rộng của nó, vì tôi không được phép triển khai thuộc tính cũng như deinitphương thức.


2

Nếu bạn có một vòng lặp các nút như tôi, bạn có thể thử một cái gì đó như thế này

var buttonTags:[Int:String]? // can be [Int:Any]
let myArray = [0:"a",1:"b"]
for (index,value) in myArray {

     let button = // Create a button

     buttonTags?[index] = myArray[index]
     button.tag = index
     button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchDown)

}
@objc func buttonAction(_ sender:UIButton) {

    let myString = buttonTags[sender.tag]

}

2

Mã Swift 4.0 (Ở đây chúng ta bắt đầu lại)

Hành động được gọi phải được đánh dấu như thế này vì đó là cú pháp của hàm nhanh để xuất các hàm sang ngôn ngữ c mục tiêu.

@objc func deleteAction(sender: UIButton) {
}

tạo một số nút làm việc:

let deleteButton = UIButton(type: .roundedRect)
deleteButton.setTitle("Delete", for: [])
deleteButton.addTarget(self, action: #selector( 
MyController.deleteAction(sender:)), for: .touchUpInside)

Cám ơn vì đã chia sẻ. Điều này hoạt động, nhưng đánh dấu nó bằng @objc cảm thấy không trực quan. Bạn có thể giải thích lý do cho điều này?
rawbee

@rawbee Đồng ý rằng điều này là phản trực giác. Câu hỏi này có nền hơn: stackoverflow.com/questions/44390378/...
Mikrasya

1

Mã Swift 5.0

Tôi sử dụng theButton.tag nhưng nếu tôi có nhiều loại tùy chọn, trường hợp chuyển đổi của nó sẽ rất dài.

theButton.addTarget(self, action: #selector(theFunc), for: .touchUpInside) 
theButton.frame.name = "myParameter"

.

@objc func theFunc(sender:UIButton){ 
print(sender.frame.name)
}

0

Đối với Swift 2.X trở lên

button.addTarget(self,action:#selector(YourControllerName.buttonClicked(_:)),
                         forControlEvents:.TouchUpInside)

0

Mã Swift 3.0

self.timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector:#selector(fetchAutocompletePlaces(timer:)), userInfo:[textView.text], repeats: true)

func fetchAutocompletePlaces(timer : Timer) {

  let keyword = timer.userInfo
}

Bạn có thể gửi giá trị trong 'userinfo' và sử dụng giá trị đó làm tham số trong hàm.


0

Đây là một nhận xét quan trọng hơn. Chia sẻ tài liệu tham khảo của sytanx có thể chấp nhận được. Đối với các giải pháp hack, hãy xem các câu trả lời khác.

Theo tài liệu của Apple, Định nghĩa phương pháp hành động phải là một trong ba định nghĩa này. Bất cứ điều gì khác không được chấp nhận.

@IBAction func doSomething()
@IBAction func doSomething(sender: UIButton)
@IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.