แนบพารามิเตอร์กับการดำเนินการ button.addTarget ใน Swift


104

ฉันพยายามส่งพารามิเตอร์พิเศษไปยังการดำเนินการ buttonClicked แต่ไม่สามารถสรุปได้ว่าไวยากรณ์ควรเป็นอย่างไรใน Swift

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

วิธี buttonClicked ของฉัน:

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

ใครมีความคิด?

ขอบคุณสำหรับความช่วยเหลือของคุณ.


1
อ้างอิงลิงค์นี้เพื่อดูคำตอบโดยละเอียดและแนวทางปฏิบัติที่ดีที่สุด
ชีทอล

คำถามไม่มีคำตอบที่ดีเนื่องจากอธิบายถึงสถาปัตยกรรมที่เป็นปัญหาอยู่แล้ว คุณไม่จำเป็นต้องมีพารามิเตอร์เพิ่มเติม ความต้องการพารามิเตอร์เพิ่มเติมเป็นปัญหาในสถาปัตยกรรมของคุณ
สุลต่าน

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

คำตอบ:


170

คุณไม่สามารถส่งผ่านพารามิเตอร์ที่กำหนดเองได้addTarget:ทางเลือกหนึ่งคือตั้งค่าtagคุณสมบัติของปุ่มและทำงานโดยยึดตามแท็ก

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

หรือสำหรับSwift 2.2ขึ้นไป:

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

ตอนนี้ทำตรรกะตามtagคุณสมบัติ

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

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

4
สวัสดีถ้าฉันต้องการรับ indexPath หรือเซลล์ของปุ่มใน tableView เป็นไปได้ไหม
NKurapati

1
คำแนะนำ: อย่าทำให้วิธีนี้เป็นส่วนตัว
OhadM

2
ดูเหมือนว่าประเภทจะเป็นIntไฟล์. ไม่มีอะไรอีกแล้ว.
Scaryguy

2
จะทำอย่างไรถ้าฉันต้องการส่งต่อแถวและส่วนด้วยกัน?
Yestay Muratov

3
เพียงแค่ให้คุณรู้ว่าแม้จะมีการโหวตเพิ่มขึ้น 118 ครั้ง (และยังเพิ่มขึ้น
เรื่อย ๆ

79

หากคุณต้องการส่งพารามิเตอร์เพิ่มเติมไปยังเมธอด buttonClicked ตัวอย่างเช่น indexPath หรือ urlString คุณสามารถ subclass UIButton:

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

อย่าลืมเปลี่ยนคลาสของปุ่มในตัวตรวจสอบเอกลักษณ์เป็น subclassedUIButton คุณสามารถเข้าถึงพารามิเตอร์ภายในวิธี buttonClicked ใช้หรือsender.indexPathsender.urlString

หมายเหตุ: หากปุ่มของคุณอยู่ภายในเซลล์คุณสามารถตั้งค่าของพารามิเตอร์เพิ่มเติมเหล่านี้ได้ในเมธอด cellForRowAtIndexPath (ที่ปุ่มถูกสร้างขึ้น)


5
นี่เป็นวิธีเดียวที่ใช้ได้ผลสำหรับฉัน แต่ฉันสงสัยว่านี่เป็นรูปแบบการต่อต้านหรือไม่?
Scaryguy

1
นี่เป็นวิธีที่ดีที่สุดฉันคิดว่า
Duy Hoang

29

ฉันขอขอบคุณทุกคนที่บอกว่าใช้แท็ก แต่จริงๆแล้วคุณต้องขยายคลาส UIButton และเพิ่มวัตถุที่นั่น ..

แท็กเป็นวิธีที่สิ้นหวังในรอบนี้ ขยาย UIButton เช่นนี้ (ใน 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)
    }
}

แล้วโทรของคุณอาจจะโทร (หมายเหตุลำไส้ใหญ่ ":" ใน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"

จากนั้นก็จับทั้งหมดที่นี่

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

คุณทำสิ่งนี้ครั้งเดียวและใช้ตลอดโครงการของคุณ (คุณสามารถทำให้คลาสย่อยมี "วัตถุ" ทั่วไปและใส่สิ่งที่คุณต้องการลงในปุ่ม!) หรือใช้ตัวอย่างด้านบนเพื่อใส่พารามิเตอร์คีย์ / สตริงจำนวนไม่สิ้นสุดลงในปุ่ม .. มีประโยชน์มากสำหรับการรวมสิ่งต่างๆเช่น URL วิธีการยืนยันข้อความ ฯลฯ

นอกจากนี้สิ่งสำคัญคือSOชุมชนต้องตระหนักถึงสิ่งนี้ว่ามีการปฏิบัติที่ไม่ดีทั้งรุ่นที่ถูกตัดและวางไว้ทั่วอินเทอร์เน็ตโดยโปรแกรมเมอร์จำนวนมากที่ไม่เข้าใจ / ไม่ได้รับการสอน / พลาดประเด็นนี้ แนวคิดของobject extensions


10

สำหรับ Swift 3.0 คุณสามารถใช้สิ่งต่อไปนี้

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

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

}

9

สวิฟต์ 4.2

ผลลัพธ์:

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

ในไฟล์UIButton+Events.swiftฉันได้สร้างวิธีการส่วนขยายสำหรับการUIButtonเชื่อมโยง a UIControl.Eventกับตัวจัดการการเสร็จสิ้นที่เรียกว่า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)
    }
}

เหตุผลที่ฉันใช้คลาสที่กำหนดเองในการเชื่อมโยงเหตุการณ์คือสามารถทิ้งการอ้างอิงในภายหลังเมื่อปุ่มถูกยกเลิกการกำหนดค่า วิธีนี้จะป้องกันไม่ให้หน่วยความจำรั่วไหลที่อาจเกิดขึ้น สิ่งนี้ไม่สามารถทำได้ภายในUIButtonส่วนขยายเนื่องจากฉันไม่ได้รับอนุญาตให้ใช้คุณสมบัติหรือdeinitวิธีการนี้


2

หากคุณมีปุ่มวนซ้ำแบบฉันคุณสามารถลองสิ่งนี้ได้

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

รหัส Swift 4.0 (ไปกันอีกแล้ว)

การดำเนินการที่เรียกว่าควรทำเครื่องหมายเช่นนี้เนื่องจากเป็นไวยากรณ์สำหรับฟังก์ชันที่รวดเร็วสำหรับการส่งออกฟังก์ชันเป็นภาษา c วัตถุประสงค์

@objc func deleteAction(sender: UIButton) {
}

สร้างปุ่มทำงาน:

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

ขอบคุณสำหรับการแบ่งปัน. วิธีนี้ใช้งานได้ แต่การทำเครื่องหมายด้วย @objc รู้สึกไม่เข้าใจง่าย คุณสามารถอธิบายเหตุผลสำหรับเรื่องนี้ได้หรือไม่?
rawbee

@rawbee ยอมรับว่านี่เป็นการตอบโต้ คำถามนี้มีความเป็นมาเพิ่มเติม: stackoverflow.com/questions/44390378/…
Mikrasya

1

รหัส Swift 5.0

ฉันใช้ theButton.tag แต่ถ้าฉันมีตัวเลือกมากมายมันเป็นเคสสวิตช์ที่ยาวมาก

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

.

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


0

รหัส 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
}

คุณสามารถส่งค่าใน 'userinfo' และใช้เป็นพารามิเตอร์ในฟังก์ชัน


0

นี่เป็นความคิดเห็นที่สำคัญกว่า การแบ่งปันการอ้างอิงของ sytanx ที่ยอมรับได้นอกกรอบ สำหรับวิธีแก้ปัญหาแฮ็คดูคำตอบอื่น ๆ

ตามเอกสารของ Apple คำจำกัดความของวิธีการดำเนินการต้องเป็นหนึ่งในสามข้อนี้ สิ่งอื่นใดไม่ได้รับการยอมรับ

@IBAction func doSomething()
@IBAction func doSomething(sender: UIButton)
@IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.