@selector () ใน Swift หรือไม่


659

ฉันพยายามที่จะสร้างNSTimerในSwiftแต่ฉันมีปัญหาบางอย่าง

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)

test() เป็นฟังก์ชั่นในชั้นเดียวกัน


ฉันพบข้อผิดพลาดในเครื่องมือแก้ไข:

ไม่พบโอเวอร์โหลดสำหรับ 'init' ที่ยอมรับอาร์กิวเมนต์ที่ให้มา

เมื่อฉันเปลี่ยนselector: test()ไปselector: nilหายไปข้อผิดพลาด

ฉันได้พยายาม:

  • selector: test()
  • selector: test
  • selector: Selector(test())

แต่ไม่มีอะไรทำงานและฉันไม่สามารถหาวิธีแก้ปัญหาในการอ้างอิง


11
selector: test()จะเรียกtestและผ่านมันส่งกลับค่าให้กับการselectorโต้แย้ง
Michael Dorst

คำตอบ:


930

สวิฟท์ตัวเองไม่ได้ใช้ตัวเลือก - รูปแบบการออกแบบหลายอย่างที่ใน Objective-C ใช้ประโยชน์จากตัวเลือกทำงานแตกต่างกันในสวิฟท์ (ตัวอย่างเช่นใช้การผูกมัดตัวเลือกกับประเภทโปรโตคอลหรือis/ asทดสอบแทนrespondsToSelector:และใช้การปิดทุกที่ที่คุณสามารถทำได้แทนperformSelector:เพื่อความปลอดภัยประเภท / หน่วยความจำที่ดีขึ้น)

แต่ยังมี API ที่ใช้ ObjC ที่สำคัญหลายตัวที่ใช้ตัวเลือกรวมถึงตัวจับเวลาและรูปแบบเป้าหมาย / การกระทำ Swift จัดเตรียมSelectorประเภทสำหรับการทำงานกับสิ่งเหล่านี้ (Swift ใช้สิ่งนี้โดยอัตโนมัติแทนประเภทของ ObjC SEL)

ใน Swift 2.2 (Xcode 7.3) และใหม่กว่า (รวมถึง Swift 3 / Xcode 8 และ Swift 4 / Xcode 9):

คุณสามารถสร้าง a Selectorจากฟังก์ชันประเภท Swift โดยใช้#selectorนิพจน์

let timer = Timer(timeInterval: 1, target: object,
                  selector: #selector(MyClass.test),
                  userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
                 for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
             with: button, with: otherButton)

สิ่งที่ดีเกี่ยวกับวิธีการนี การอ้างอิงฟังก์ชั่นถูกตรวจสอบโดยคอมไพเลอร์ Swift ดังนั้นคุณสามารถใช้#selectorนิพจน์กับคู่คลาส / วิธีที่มีอยู่จริงและมีสิทธิ์ใช้งานเป็นตัวเลือก (ดู "ความพร้อมใช้ของตัวเลือก" ด้านล่าง) คุณยังมีอิสระที่จะทำให้การอ้างอิงฟังก์ชั่นของคุณเท่านั้นที่เป็นเฉพาะที่คุณต้องการให้เป็นไปตามกฎระเบียบที่สวิฟท์ 2.2 ขึ้นไปสำหรับการทำงานประเภทการตั้งชื่อ

(นี่เป็นการปรับปรุง@selector()คำสั่งของ ObjC เนื่องจากการ-Wundeclared-selectorตรวจสอบคอมไพเลอร์ตรวจสอบเฉพาะว่าตัวเลือกที่มีชื่ออยู่แล้วการอ้างอิงฟังก์ชัน Swift ที่คุณส่งผ่านไปยังการ#selectorตรวจสอบการมีอยู่เป็นสมาชิกในชั้นเรียนและประเภทลายเซ็น)

มีข้อควรระวังเพิ่มเติมสองสามประการสำหรับการอ้างอิงฟังก์ชันที่คุณส่งผ่านไปยัง#selectorนิพจน์:

  • ฟังก์ชั่นหลายอย่างที่มีชื่อฐานเดียวกันสามารถสร้างความแตกต่างได้โดยใช้ชื่อพารามิเตอร์โดยใช้ไวยากรณ์ข้างต้นสำหรับการอ้างอิงฟังก์ชั่น (เช่นinsertSubview(_:at:)vs insertSubview(_:aboveSubview:)) แต่ถ้าฟังก์ชั่นไม่มีพารามิเตอร์วิธีเดียวที่จะทำให้กระจ่างก็คือการใช้ตัวasละครที่มีลายเซ็นประเภทของฟังก์ชั่น (เช่นfoo as () -> ()vs foo(_:))
  • มีไวยากรณ์พิเศษสำหรับคู่คุณสมบัติ getter / setter ใน Swift 3.0+ ยกตัวอย่างเช่นที่กำหนดvar foo: Intคุณสามารถใช้หรือ#selector(getter: MyClass.foo)#selector(setter: MyClass.foo)

บันทึกทั่วไป:

กรณีที่#selectorไม่ได้ผลและการตั้งชื่อ:บางครั้งคุณไม่มีการอ้างอิงฟังก์ชั่นเพื่อสร้างตัวเลือกด้วย (ตัวอย่างเช่นด้วยวิธีการที่ลงทะเบียนแบบไดนามิกใน ObjC runtime) ในกรณีนั้นคุณสามารถสร้าง a Selectorจากสตริง: เช่นSelector("dynamicMethod:")- แม้ว่าคุณจะสูญเสียการตรวจสอบความถูกต้องของคอมไพเลอร์ เมื่อคุณทำเช่นนั้นคุณจะต้องปฏิบัติตามกฎการตั้งชื่อ ObjC รวมถึง colons ( :) สำหรับแต่ละพารามิเตอร์

ความพร้อมใช้ของตัวเลือก:วิธีการอ้างอิงโดยตัวเลือกต้องเปิดเผยกับ ObjC runtime ใน Swift 4 ทุกวิธีที่เปิดเผยกับ ObjC จะต้องมีการประกาศ@objcล่วงหน้าด้วยแอตทริบิวต์ (ในเวอร์ชันก่อนหน้าคุณมีแอททริบิวต์นั้นฟรีในบางกรณี แต่ตอนนี้คุณต้องประกาศอย่างชัดเจน)

โปรดจำไว้ว่าprivateสัญลักษณ์นั้นไม่ได้ปรากฏในรันไทม์เช่นกัน - วิธีการของคุณต้องมีinternalการเปิดเผยอย่างน้อย

เส้นทางหลัก: สิ่งเหล่านี้เกี่ยวข้องกับ แต่ไม่เหมือนกับตัวเลือก มีไวยากรณ์พิเศษเหล่านี้อยู่ในสวิฟท์ 3 เกินไป: chris.valueForKeyPath(#keyPath(Person.friends.firstName))เช่น ดูSE-0062สำหรับรายละเอียด และKeyPathสิ่งอื่น ๆ อีกมากมายใน Swift 4ดังนั้นตรวจสอบให้แน่ใจว่าคุณใช้ API ที่ใช้ KeyPath ที่ถูกต้องแทนตัวเลือกตามความเหมาะสม

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับตัวเลือกภายใต้การมีปฏิสัมพันธ์กับ APIs Objective-Cในการใช้สวิฟท์กับโกโก้และวัตถุประสงค์ -C

หมายเหตุ:ก่อน Swift 2.2 Selectorเป็นไปตามStringLiteralConvertibleดังนั้นคุณอาจพบรหัสเก่าที่มีการส่งสตริงเปลือยไปยัง API ที่ใช้ตัวเลือก คุณจะต้องการที่จะเรียกใช้ "แปลงปัจจุบัน Swift ไวยากรณ์" ใน Xcode #selectorเพื่อให้ได้ผู้ที่ใช้


8
การใส่สตริงด้วยชื่อฟังก์ชั่นการทำงาน NSSelectorFromString () ก็ใช้งานได้เช่นกัน
Arbitur

7
ฉันต้องการพูดถึงว่าในขณะที่ "การโต้ตอบกับ Objective-C APIs" อยู่บนเว็บไซต์มันไม่ได้อยู่ในหนังสือ 'The Swift Programming Language'

10
สิ่งนี้น่าจะกล่าวถึงว่าตัวเลือกต้องการ ":" ที่ท้ายถ้ามีการโต้แย้ง (เช่นทดสอบ () -> "ทดสอบ" & ทดสอบ (นี่: สตริง) -> "ทดสอบ:")
แดเนียล Schlaug

2
มันควรจะชี้ให้เห็นว่ากรอบ Cocoa คาดว่าชื่อวิธีสไตล์ Objective-C หากวิธีการของคุณใช้เวลาทะเลาะกันคุณจะต้องมี ':' ถ้ามันใช้เวลา 2 ข้อโต้แย้ง size:andShape:ถ้าอาร์กิวเมนต์แรกเป็นชื่อคุณอาจต้องWithคือinitWithData:สำหรับfunc init(Data data: NSData)
JMFR

6
มีอยู่แล้วเพื่อเพิ่มการตรวจสอบรอบผ่าน "ตัวเลือก" เป็นสตริงหรือไม่ คอมไพเลอร์ IE เตือนเราเมื่อเราสะกดคำผิด ฯลฯ
yo.ian.g

86

นี่คือตัวอย่างย่อเกี่ยวกับวิธีใช้Selectorคลาสใน Swift:

override func viewDidLoad() {
    super.viewDidLoad()

    var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
    self.navigationItem.rightBarButtonItem = rightButton
}

func method() {
    // Something cool here   
}

โปรดทราบว่าหากวิธีการส่งผ่านเป็นสตริงไม่ทำงานมันจะล้มเหลวขณะใช้งานไม่ใช่เวลารวบรวมและทำให้แอปของคุณทำงานล้มเหลว ระวัง


13
อันไหนที่น่ากลัว ... มีสิ่งของประเภท "NSStringFromSelector" ไหม?
Lena Bru

11
ไม่สามารถเชื่อได้ว่าพวกเขาออกแบบมาสำหรับตัวเลือกที่ไม่ถูกตรวจสอบเนื่องจาก objc มีสิ่งนี้
malhal

4
@malcomhall: @selectorมีประโยชน์ แต่ก็ไม่ได้บังคับใช้อย่างเป็นทางการอย่างที่คุณคิด "ตัวเลือกที่ไม่ได้ประกาศ" เป็นเพียงคำเตือนจากคอมไพเลอร์เนื่องจากตัวเลือกใหม่สามารถนำมาใช้ในเวลาทำงานได้เสมอ แม้ว่าการอ้างอิงของตัวเลือกที่ตรวจสอบได้ / การปรับโครงสร้างใน Swift จะเป็นการร้องขอคุณสมบัติที่ดี
rickster

2
คำตอบนี้มีประโยชน์ แต่คำตอบด้านล่างด้วย @objc เหมาะสมกว่า
cynistersix

เมื่อคุณส่งสตริงตัวเลือกเป็นตัวแปรหรือพารามิเตอร์คุณจะต้องให้คอมไพเลอร์ทราบถึงตัวเลือกโดยใช้ฟังก์ชัน Selector () ขอบคุณ
เก็บกู้

46

นอกจากนี้หากคลาส (Swift) ของคุณไม่ได้สืบทอดมาจากคลาส Objective-C คุณจะต้องมีโคลอนที่ส่วนท้ายของสตริงชื่อเมธอดเป้าหมายและคุณต้องใช้คุณสมบัติ @objc ด้วยวิธีการเป้าหมายของคุณเช่น

var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))

@objc func method() {
    // Something cool here   
} 

มิฉะนั้นคุณจะได้รับข้อผิดพลาด "ตัวเลือกที่ไม่รู้จัก" ขณะใช้งานจริง


3
1. ตัวเลือกที่มีโคลอนจะต้องโต้แย้ง 2. ตามการกระทำของตัวจับเวลาของ Apple docs ควรใช้อาร์กิวเมนต์ NSTimer 3. Selectorคำสำคัญไม่จำเป็น ดังนั้นในกรณีนี้จะต้องเป็นลายเซ็น@objc func method(timer: NSTimer) {/*code*/}
Yevhen Dubinin

@objcทำงานให้ฉัน ฉันไม่จำเป็นต้องรวมtimer: NSTimerไว้ในลายเซ็นวิธีการเพื่อให้เรียกใช้
Mike Taverne

32

Swift 2.2+ และ Swift 3 Update

ใช้#selectorนิพจน์ใหม่ซึ่งไม่จำเป็นต้องใช้ตัวอักษรสตริงทำให้การใช้งานเกิดข้อผิดพลาดน้อยลง สำหรับการอ้างอิง:

Selector("keyboardDidHide:")

กลายเป็น

#selector(keyboardDidHide(_:))

ดูเพิ่มเติม: Swift Evolution Proposal

หมายเหตุ (Swift 4.0):

หากใช้#selectorคุณจะต้องทำเครื่องหมายฟังก์ชั่นเป็น@objc

ตัวอย่าง:

@objc func something(_ sender: UIButton)


26

Swift 4.0

คุณสร้างตัวเลือกเช่นด้านล่าง

1. เพิ่มเหตุการณ์ไปยังปุ่มเช่น:

button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)

และฟังก์ชั่นจะเป็นดังนี้:

@objc func clickedButton(sender: AnyObject) {

}

4
คุณลืมที่จะใส่@objcก่อนfuncซึ่งเป็นสิ่งจำเป็นใน Swift 4
Vahid Amiri

24

สำหรับผู้อ่านในอนาคตฉันพบว่าฉันประสบปัญหาและได้รับunrecognised selector sent to instanceข้อผิดพลาดที่เกิดจากการทำเครื่องหมายเป้าหมายfuncเป็นส่วนตัว

func ต้องปรากฏต่อสาธารณะที่จะเรียกได้ว่าเป็นวัตถุที่มีการอ้างอิงไปยังผู้เลือก


13
มันไม่จำเป็นต้องเป็นแบบสาธารณะคุณยังสามารถเก็บวิธีการเป็นส่วนตัว แต่เพิ่มobjcก่อนที่จะประกาศ ตัวอย่าง: @objc private func foo() { ...จากนั้นคุณสามารถใช้"foo"เป็นตัวเลือกได้มากเท่าที่คุณต้องการ
apouche

อาจเป็นinternalเช่นนั้นได้ดังนั้นจึงไม่ได้ระบุตัวดัดแปลงการเข้าถึง ฉันมักจะใช้รูปแบบนี้://MARK: - Selector Methods\n extension MyController {\n func buttonPressed(_ button: UIButton) {
Sajjon

19

ในกรณีที่คนอื่นมีปัญหาเดียวกันกับ NSTimer โดยที่ไม่มีคำตอบอื่นใดแก้ไขปัญหาเป็นสิ่งสำคัญที่จะพูดถึงว่าหากคุณใช้คลาสที่ไม่ได้รับมรดกจาก NSObject โดยตรงหรือลึกลงไปในลำดับชั้น ( เช่นสร้างไฟล์ swift ด้วยตนเอง) ไม่มีคำตอบอื่นใดที่สามารถใช้งานได้แม้จะระบุไว้ดังนี้:

let timer = NSTimer(timeInterval: 1, target: self, selector: "test", 
                    userInfo: nil, repeats: false)
func test () {}

โดยไม่ต้องเปลี่ยนอะไรอื่นนอกจากเพียงแค่ทำให้คลาสสืบทอดจาก NSObject ฉันหยุดรับข้อผิดพลาด "ตัวเลือกที่ไม่รู้จัก" และทำให้ตรรกะของฉันทำงานตามที่คาดไว้


ปัญหาเกี่ยวกับทางเลือกนี้คือคุณไม่สามารถเปลี่ยนคลาส (สมมติว่า ViewController) เป็นสืบทอดจาก NSObject เนื่องจากคุณต้องใช้คลาส ViewController (เช่น viewDidLoad ()) มีความคิดวิธีการเรียกใช้ฟังก์ชั่น Swift ภายใน ViewController โดยใช้ NSTimer หรือไม่ ... e
eharo2

1
UIViewController แล้วสืบทอดจาก NSObject เรียนมากที่สุดเปิดเผยโดย SDK ทำตัวอย่างนี้เป็นสำหรับการเรียนของตัวเองสร้างขึ้นที่จำเป็นต้องมีการทำงาน NSTimer ...
มาร์ติน Cazares

14

หากคุณต้องการส่งผ่านพารามิเตอร์ไปยังฟังก์ชันจาก NSTimer นี่คือทางออกของคุณ:

var somethingToPass = "It worked"

let timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "tester:", userInfo: somethingToPass, repeats: false)

func tester(timer: NSTimer)
{
    let theStringToPrint = timer.userInfo as String
    println(theStringToPrint)
}

รวมเครื่องหมายโคลอนในข้อความตัวเลือก (ผู้ทดสอบ :) และพารามิเตอร์ของคุณจะอยู่ใน userInfo

ฟังก์ชันของคุณควรใช้ NSTimer เป็นพารามิเตอร์ จากนั้นให้แยก userInfo เพื่อรับพารามิเตอร์ที่ผ่าน


3
ฉันใช้ NSTimer (0.01, target: self, ... ) ซึ่งใช้งานไม่ได้ในขณะที่ใช้ NSTimer.scheduledTimerWithTimeInterval (0.01, .. ) DID ทำงาน!? แปลก แต่ขอบคุณ @Scooter สำหรับคุณตอบ!
iOS-Coder

1
@ iOS-Coder เพิ่งสร้างตัวจับเวลาด้วย initialiser ไม่เพิ่มลงใน runloop ในขณะที่scheduledTimerWith...เพิ่มโดยอัตโนมัติใน runloop ปัจจุบัน - ดังนั้นไม่มีพฤติกรรมแปลก ๆ ที่นี่เลย)
David Ganster

1
@ David ขอบคุณสำหรับคำแนะนำของคุณ ฉันเดาว่าความเข้าใจผิดของฉันควรอยู่ในหมวดหมู่ STFW หรือ RTFA (อ่าน The F ... ing API)?
iOS-Coder

1
ไม่ต้องกังวลเกี่ยวกับมันไม่มีใครคาดได้ว่าจะอ่านเอกสารเกี่ยวกับทุก ๆ วิธีในทุก ๆ API)
David Ganster

10

Selectors เป็นการแสดงภายในของชื่อเมธอดใน Objective-C ใน Objective-C "@selector (methodName)" จะแปลงวิธีการซอร์สโค้ดเป็นชนิดข้อมูลของ SEL เนื่องจากคุณไม่สามารถใช้ไวยากรณ์ @selector ใน Swift ได้ (rickster อยู่ที่จุดนั้น) คุณจะต้องระบุชื่อเมธอดเป็นวัตถุ String ด้วยตนเองโดยตรงหรือส่งวัตถุ String ไปยังประเภท Selector นี่คือตัวอย่าง:

var rightBarButton = UIBarButtonItem(
    title: "Logout", 
    style: UIBarButtonItemStyle.Plain, 
    target: self, 
    action:"logout"
)

หรือ

var rightBarButton = UIBarButtonItem(
    title: "Logout", 
    style: UIBarButtonItemStyle.Plain, 
    target: self, 
    action:Selector("logout")
)

8

Swift 4.1
พร้อมตัวอย่างท่าทางแตะ

let gestureRecognizer = UITapGestureRecognizer()
self.view.addGestureRecognizer(gestureRecognizer)
gestureRecognizer.addTarget(self, action: #selector(self.dismiss(completion:)))

// Use destination 'Class Name' directly, if you selector (function) is not in same class.
//gestureRecognizer.addTarget(self, action: #selector(DestinationClass.dismiss(completion:)))


@objc func dismiss(completion: (() -> Void)?) {
      self.dismiss(animated: true, completion: completion)
}

ดูเอกสารของ Apple สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ: ตัวเลือกการแสดงออก


โปรดหยุดทำสิ่งนี้ มันช่วยไม่มีใคร สิ่งนี้แตกต่างจาก Swift 3.1 อย่างไร และทำไมคุณถึงคิดว่าจำเป็นต้องเพิ่มคำตอบอีกครั้งเมื่อมีประมาณ 20 คำตอบ?
Fogmeister

ตัวเลือกการโทรแตกต่างกันใน swift 4 ลองใช้คำตอบเหล่านี้ใน swift 4 และดู ไม่มีสิ่งเหล่านี้จะทำงานโดยไม่ต้องแก้ไข โปรดอย่าทำเครื่องหมายคำสั่งใด ๆ ว่าเป็นจดหมายขยะโดยไม่ทำให้แน่ใจว่าไม่มีสมรรถภาพ
Krunal

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

นอกจากนี้ส่วนใดของสิ่งนี้แตกต่างจาก Swift 3
Fogmeister

2
คุณต้องเพิ่มแท็ก objc ไปยังตัวเลือกใด ๆ สำหรับ Swift 4 นี่คือคำตอบที่ถูกต้อง และคุณไม่ควรแก้ไขคำตอบของคนอื่นเพื่อเปลี่ยนความหมายของพวกเขา @Krunal นั้นถูกต้องแล้ว
Unome

6
// for swift 2.2
// version 1
buttton.addTarget(self, action: #selector(ViewController.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(ViewController.tappedButton2(_:)), forControlEvents: .TouchUpInside)

// version 2
buttton.addTarget(self, action: #selector(self.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(self.tappedButton2(_:)), forControlEvents: .TouchUpInside)

// version 3
buttton.addTarget(self, action: #selector(tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(tappedButton2(_:)), forControlEvents: .TouchUpInside)

func tappedButton() {
  print("tapped")
}

func tappedButton2(sender: UIButton) {
  print("tapped 2")
}

// swift 3.x
button.addTarget(self, action: #selector(tappedButton(_:)), for: .touchUpInside)

func tappedButton(_ sender: UIButton) {
  // tapped
}

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

func tappedButton(_ sender: UIButton, _ event: UIEvent) {
  // tapped
}

มันคงจะดีกว่าและให้ความรู้มากกว่านี้หากคุณมีตัวอย่างการโต้แย้งสองหรือสามข้อสำหรับ Swift3 หรือ Swift4 เช่นกัน ขอบคุณ
nyxee

5
Create Refresh control using Selector method.   
    var refreshCntrl : UIRefreshControl!
    refreshCntrl = UIRefreshControl()
    refreshCntrl.tintColor = UIColor.whiteColor()
    refreshCntrl.attributedTitle = NSAttributedString(string: "Please Wait...")
    refreshCntrl.addTarget(self, action:"refreshControlValueChanged", forControlEvents: UIControlEvents.ValueChanged)
    atableView.addSubview(refreshCntrl)

// วิธีควบคุมรีเฟรช

func refreshControlValueChanged(){
    atableView.reloadData()
    refreshCntrl.endRefreshing()

}

5

เนื่องจาก Swift 3.0 ได้รับการเผยแพร่จึงเป็นเรื่องที่ละเอียดยิ่งขึ้นอีกเล็กน้อยในการประกาศ targetAction ที่เหมาะสม

class MyCustomView : UIView {

    func addTapGestureRecognizer() {

        // the "_" is important
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyCustomView.handleTapGesture(_:)))
        tapGestureRecognizer.numberOfTapsRequired = 1
        addGestureRecognizer(tapGestureRecognizer)
    }

    // since Swift 3.0 this "_" in the method implementation is very important to 
    // let the selector understand the targetAction
    func handleTapGesture(_ tapGesture : UITapGestureRecognizer) {

        if tapGesture.state == .ended {
            print("TapGesture detected")
        }
    }
}

4

เมื่อใช้งาน performSelector()

/addtarget()/NStimer.scheduledTimerWithInterval() เมธอดของคุณ (ตรงกับตัวเลือก) ควรถูกทำเครื่องหมายเป็น

@objc
For Swift 2.0:
    {  
        //...
        self.performSelector(“performMethod”, withObject: nil , afterDelay: 0.5)
        //...


    //...
    btnHome.addTarget(self, action: “buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
    //...

    //...
     NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector : “timerMethod”, userInfo: nil, repeats: false)
    //...

}

@objc private func performMethod() {}
@objc private func buttonPressed(sender:UIButton){.
}
@objc private func timerMethod () {.
}

สำหรับ Swift 2.2 คุณต้องเขียน '#selector ()' แทนสตริงและชื่อตัวเลือกดังนั้นความเป็นไปได้ของการสะกดคำผิดพลาดและความผิดพลาดเนื่องจากจะไม่เกิดขึ้นอีก ด้านล่างเป็นตัวอย่าง

self.performSelector(#selector(MyClass.performMethod), withObject: nil , afterDelay: 0.5)

3

คุณสร้างตัวเลือกเช่นด้านล่าง
1

UIBarButtonItem(
    title: "Some Title",
    style: UIBarButtonItemStyle.Done,
    target: self,
    action: "flatButtonPressed"
)

2

flatButton.addTarget(self, action: "flatButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)

โปรดทราบว่าไวยากรณ์ @selector หายไปและแทนที่ด้วยสตริงแบบง่ายตั้งชื่อวิธีการโทร มีพื้นที่หนึ่งที่เราทุกคนสามารถเห็นด้วยกับการใช้คำฟุ่มเฟือย แน่นอนถ้าเราประกาศว่ามีวิธีการเป้าหมายที่เรียกว่า flatButtonPressed: เราควรเขียนหนึ่งวิธี:

func flatButtonPressed(sender: AnyObject) {
  NSLog("flatButtonPressed")
}

ตั้งเวลา:

    var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, 
                    target: self, 
                    selector: Selector("flatButtonPressed"), 
                    userInfo: userInfo, 
                    repeats: true)
    let mainLoop = NSRunLoop.mainRunLoop()  //1
    mainLoop.addTimer(timer, forMode: NSDefaultRunLoopMode) //2 this two line is optinal

เพื่อที่จะเสร็จสมบูรณ์นี่คือปุ่มแบนกด

func flatButtonPressed(timer: NSTimer) {
}

คุณมีแหล่งที่มาของ " จดบันทึกว่า @selector ไวยากรณ์หายไป " หรือไม่
winklerrr

3

ฉันพบคำตอบมากมายเหล่านี้จะเป็นประโยชน์ แต่ก็ไม่ชัดเจนว่าจะทำอย่างไรกับสิ่งที่ไม่ใช่ปุ่ม ฉันกำลังเพิ่มตัวจดจำท่าทางไปยัง UILabel อย่างรวดเร็วและดิ้นรนดังนั้นนี่คือสิ่งที่ฉันพบว่าทำงานได้ดีสำหรับฉันหลังจากอ่านทุกอย่างด้านบน:

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: "labelTapped:")

ตำแหน่งที่ "Selector" ถูกประกาศเป็น:

func labelTapped(sender: UILabel) { }

โปรดทราบว่ามันเป็นแบบสาธารณะและฉันไม่ได้ใช้ไวยากรณ์ Selector () แต่ก็สามารถทำได้เช่นกัน

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: Selector("labelTapped:"))

3

การใช้#selectorจะตรวจสอบรหัสของคุณในเวลารวบรวมเพื่อให้แน่ใจว่าวิธีที่คุณต้องการโทรมีอยู่จริง ยิ่งไปกว่านั้นหากวิธีนี้ไม่มีอยู่คุณจะได้รับข้อผิดพลาดในการคอมไพล์: Xcode จะปฏิเสธที่จะสร้างแอปของคุณ

override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.rightBarButtonItem =
            UIBarButtonItem(barButtonSystemItem: .Add, target: self,
                            action: #selector(addNewFireflyRefernce))
    }

    func addNewFireflyReference() {
        gratuitousReferences.append("Curse your sudden but inevitable betrayal!")
    }

2

อาจเป็นประโยชน์ที่จะทราบว่าคุณตั้งค่าการควบคุมที่ก่อให้เกิดการกระทำที่สำคัญ

ตัวอย่างเช่นฉันพบว่าเมื่อตั้งค่า UIBarButtonItem ฉันต้องสร้างปุ่มภายใน viewDidLoad มิฉะนั้นฉันจะได้รับข้อยกเว้นตัวเลือกที่ไม่รู้จัก

override func viewDidLoad() {
    super.viewDidLoad() 

    // add button
    let addButton = UIBarButtonItem(image: UIImage(named: "746-plus-circle.png"), style: UIBarButtonItemStyle.Plain, target: self, action: Selector("addAction:"))
    self.navigationItem.rightBarButtonItem = addButton
}

func addAction(send: AnyObject?) {     
    NSLog("addAction")
}

1

เปลี่ยนเป็นการตั้งชื่อสตริงแบบธรรมดาในวิธีการเรียกใช้ไวยากรณ์ของตัวเลือก

var timer1 : NSTimer? = nil
timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true)

หลังจากนั้นให้พิมพ์ func test ()


1

selectorเป็นคำจากObjective-Cโลกและคุณสามารถใช้จากSwiftเพื่อให้มีความเป็นไปได้ที่จะโทรObjective-CจากSwift มันช่วยให้คุณสามารถรันรหัสบางอย่างที่รันไทม์

ก่อนSwift 2.2ไวยากรณ์คือ:

Selector("foo:")

เนื่องจากชื่อฟังก์ชันถูกส่งผ่านSelectorเป็นพารามิเตอร์ String ("foo") จึงไม่สามารถตรวจสอบชื่อในเวลาคอมไพล์ได้ ดังนั้นคุณจะได้รับข้อผิดพลาด runtime:

unrecognized selector sent to instance

หลังจากSwift 2.2+ไวยากรณ์คือ:

#selector(foo(_:))

การเติมข้อความอัตโนมัติของ Xcode ช่วยให้คุณโทรหาวิธีที่ถูกต้อง


0

สำหรับ Swift 3

// ตัวอย่างรหัสเพื่อสร้างตัวจับเวลา

Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(updateTimer)), userInfo: nil, repeats: true)

WHERE
timeInterval:- Interval in which timer should fire like 1s, 10s, 100s etc. [Its value is in secs]
target:- function which pointed to class. So here I am pointing to current class.
selector:- function that will execute when timer fires.

func updateTimer(){
    //Implemetation 
} 

repeats:- true/false specifies that timer should call again n again.


-1

สำหรับสวิฟท์ 3

let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.test), userInfo: nil, repeats: true)

ประกาศฟังก์ชั่นในชั้นเดียวกัน:

@objc func test()
{
    // my function
} 

หากเป้าหมายเป็นของตัวเองก็ไม่จำเป็นต้องมีselfในตัวเลือก นี่น่าจะพอ:let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(test), userInfo: nil, repeats: true)
Mithra Singam
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.