swift - selector()في سويفت؟




nstimer (17)

أحاول إنشاء 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())

لكن لا شيء يعمل ولا أستطيع إيجاد حل في المراجع.


Answers

تعتبر المحددات عبارة عن تمثيل داخلي لاسم أسلوب في 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")
)

سويفت 2.2+ وتحديث سويفت 3

استخدم التعبير #selector الجديد ، الذي يلغي الحاجة إلى استخدام القيم الحرفية للسلسلة مما يجعل الاستخدام أقل عرضة للخطأ. كمرجع:

Selector("keyboardDidHide:")

يصبح

#selector(keyboardDidHide(_:))

انظر أيضا: مقترح تطور سريع

ملاحظة (سويفت 4.0):

إذا كنت تستخدم #selector فستحتاج إلى وضع علامة على الوظيفة باسم @objc

مثال:

@objc func something(_ sender: UIButton)


لا تستخدم سويفت نفسها محددات - العديد من أنماط التصميم التي تستخدم في Objective-C استخدام المحددات بطريقة مختلفة في سويفت. (على سبيل المثال ، استخدم تسلسلًا اختياريًا على أنواع البروتوكول أو في is / كاختبارات بدلاً من respondsToSelector: performSelector: ، واستخدم الإغلاق أينما كنت بدلاً من performSelector: لضمان سلامة أفضل للذاكرة / النوع.)

ولكن لا يزال هناك عدد من واجهات برمجة التطبيقات (API) المهمة المستندة إلى ObjC والتي تستخدم أدوات التحديد ، بما في ذلك المؤقت ونمط الهدف / الإجراء. يوفر Swift نوع Selector للعمل مع هذه. (يستخدم Swift تلقائيًا هذا بدلاً من نوع SEL الخاص بـ ObjC.)

في Swift 2.2 (Xcode 7.3) والإصدارات الأحدث (بما في ذلك Swift 3 / Xcode 8 و Swift 4 / Xcode 9):

يمكنك إنشاء 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 فقط مع أزواج الفئة / الطريقة الموجودة بالفعل وتكون مؤهلة للاستخدام كمحددات (انظر "توافر محدد" أدناه). أنت أيضًا مطلق الحرية في جعل مرجع الدالة الخاص بك محددًا بقدر ما تحتاج إليه ، وفقًا لقواعد Swift 2.2 + الخاصة بتسمية نوع الوظيفة .

(يعد هذا في الواقع تحسناً على توجيه @selector() ، لأن التحقق من -Wundeclared-selector يتحقق من أن المحدد المحدد موجود فقط. تقوم الدالة Swift بالرجوع إلى #selector يتحقق ، والعضوية في فصل #selector ، واكتب التوقيع .)

هناك بعض التحذيرات الإضافية لمراجع الوظائف التي تمررها إلى تعبير #selector :

  • يمكن تمييز الوظائف المتعددة التي لها نفس الاسم الأساسي من خلال تصنيفات المعلمات الخاصة بها باستخدام البنية المذكورة أعلاه لمراجع الوظائف (مثل insertSubview(_:at:) مقابل insertSubview(_:aboveSubview:) ). ولكن إذا لم يكن للوظيفة أية معلمات ، فإن الطريقة الوحيدة لإزالة الغموض هي استخدام الحرف المصبوب مع توقيع نوع الدالة (على سبيل المثال foo as () -> () مقابل foo(_:) ).
  • هناك بنية خاصة لأزواج الممتلكات / مضيف الملكية في Swift 3.0+. على سبيل المثال ، في ضوء var foo: Int ، يمكنك استخدام #selector(getter: MyClass.foo) أو #selector(setter: MyClass.foo) .

ملاحظات عامة:

الحالات التي لا يعمل فيها #selector ، والتسمية: في بعض الأحيان لا يكون لديك مرجع دالة لإنشاء محدد (على سبيل المثال ، مع الطرق المسجلة بشكل ديناميكي في وقت تشغيل ObjC). في هذه الحالة ، يمكنك إنشاء Selector من سلسلة: على سبيل المثال Selector("dynamicMethod:") - على الرغم من أنك تفقد التحقق من صلاحية المترجم. عند القيام بذلك ، تحتاج إلى اتباع قواعد تسمية ObjC ، بما في ذلك colons (:) لكل معلمة.

توفر محدد: يجب أن يتم عرض الطريقة المشار إليها بواسطة المحدد وقت التشغيل ObjC. في Swift 4 ، يجب أن يكون لكل طريقة معرضة لـ ObjC تصريحها مع سمة @objc . (في الإصدارات السابقة ، حصلت على هذه السمة مجانًا في بعض الحالات ، ولكن عليك الآن الإعلان عنها صراحةً.)

تذكر أن الرموز private غير معرّضة لوقت التشغيل أيضًا - تحتاج طريقتك إلى رؤية internal على الأقل.

المسارات الرئيسية: ترتبط ببعضها ولكن لا تختلف تمامًا عن المحددات. هناك صيغة خاصة لهذه في Swift 3 ، أيضا: على سبيل المثال chris.valueForKeyPath(#keyPath(Person.friends.firstName)) . انظر SE-0062 لمزيد من التفاصيل. بالإضافة إلى المزيد من عناصر KeyPath في Swift 4 ، تأكد من أنك تستخدم واجهة برمجة التطبيقات (API) القائمة على KeyPath بدلاً من المحددات إذا كان ذلك مناسبًا.

يمكنك قراءة المزيد عن المحددات الموجودة تحت Interacting with Objective-C APIs باستخدام Swift مع Cocoa و Objective-C .

ملاحظة: قبل Swift 2.2 ، يتطابق Selector مع StringLiteralConvertible ، لذا قد تجد رمزًا قديمًا يتم فيه تمرير سلاسل عارية إلى واجهات برمجة التطبيقات (APIs) التي تأخذ المحددات. ستحتاج إلى تشغيل "التحويل إلى Current Swift Syntax" في Xcode للحصول على أولئك الذين يستخدمون #selector .


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

}

فقط في حالة ما إذا كان لدى شخص آخر نفس المشكلة التي واجهتها مع NSTimer حيث لم يتم حل أي من الإجابات الأخرى المشكلة ، من المهم جدًا الإشارة إلى ذلك ، إذا كنت تستخدم فئة لا ترث من NSObject إما بشكل مباشر أو عميق في التسلسل الهرمي ( على سبيل المثال ملفات swift تم إنشاؤها يدويًا) ، لن تعمل أي من الإجابات الأخرى حتى عند تحديدها على النحو التالي:

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

دون تغيير أي شيء آخر غير مجرد ترث الطبقة من NSObject توقفت عن الحصول على خطأ "Unrecognized selector" وحصلت على منطق عملي كما هو متوقع.


أيضاً ، إذا لم تنحدر فئة (سويفت) الخاصة بك من فئة Objective-C ، فيجب أن يكون لديك نقطتان في نهاية سلسلة اسم الأسلوب المستهدف ويجب عليك استخدام الخاصيةobjc بالطريقة التي تستهدفها مثل

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

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

وإلا ستحصل على خطأ "Unrecognized Selector" في وقت التشغيل.


// 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
}

باستخدام #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!")
    }

سويفت 4.0

قمت بإنشاء محدد مثل أدناه.

1. إضافة الحدث إلى زر مثل:

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

وستكون الوظيفة كما يلي:

@objc func clickedButton(sender: AnyObject) {

}

بالنسبة إلى القراء المستقبليين ، اكتشفت أنني واجهت مشكلة وكنت أتلقى unrecognised selector sent to instance الخطأ التي حدثت بسبب وضع علامة على الدالة func كملف خاص.

يجب أن يكون func مرئيًا بشكل علني ليتم استدعاؤه بواسطة كائن مع الإشارة إلى محدد.


قد يكون من المفيد ملاحظة المكان الذي تقوم فيه بإعداد عنصر التحكم الذي يشغل مسائل الإجراء.

على سبيل المثال ، لقد وجدت أنه عند إعداد 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")
    }

تغيير كتسمية سلسلة بسيطة في طريقة استدعاء بناء جملة selector

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

بعد ذلك ، اكتب اختبار func ().


لقد وجدت أن العديد من هذه الإجابات مفيدة ولكن لم يكن من الواضح كيفية القيام بذلك باستخدام شيء لم يكن زرًا. كنت أقوم بإضافة أداة التعرف على الإيماءات إلى UILabel بسرعة وكفاح حتى هنا ما وجدته عملت لي بعد قراءة كل شيء أعلاه:

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

حيث تم الإعلان عن "Selector" على النحو التالي:

func labelTapped(sender: UILabel) { }

لاحظ أنه عام وأنني لا أستخدم صيغة Selector () ولكن من الممكن القيام بذلك أيضًا.

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

سويفت 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 للحصول على مزيد من التفاصيل حول: Selector Expression


منذ نشر 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")
        }
    }
}

إذا كنت ترغب في تمرير المعلمة إلى الدالة من 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 للحصول على المعلمة التي تم تمريرها.


تشير الشريحة إلى الصفيف. لا توجد نقطة جعل صفيف آخر عندما المصفوفة موجودة بالفعل ويمكن للشريحة فقط وصف الجزء المطلوب منه.

تؤدي الإضافة إلى الإكراه الضمني ، لذا فهي تعمل. لإنجاح مهمتك ، ستحتاج إلى إكراه:

var list = ["hello", "world"]
var slice: Array<String> = Array(list[0..<list.count])




swift selector nstimer