2016-07-30 4 views
15

Super newb w rozwoju Swift i iOS tutaj."classname nie ma nazwy funkcji member" podczas dodawania celu UIButton

Podążam za this tutorial informacjami o implementacji niestandardowego elementu sterującego w jednym widoku aplikacji na iOS. Jest to samouczek Swift 2, ale do tej pory robię OK, transponując wszystko na 3, kiedy jestem (używam XCode 8 Beta).

Mam niestandardową klasę, RatingControl, połączoną z View w scenorysie.

W konstruktorze klasy, w I utworzyć przycisk:

let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44)) 
button.backgroundColor = UIColor.red() 

Następnie próbuję przypisać akcję do przycisku. Samouczek mówi, że powinniśmy to zrobić tak:

button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), 
for: .touchDown)  

a następnie utworzyć w tym samym RatingControl klasie, metoda:

func ratingButtonTapped(button: UIButton) { 
    print("Button pressed ") 
} 

Ale kiedy mogę skompilować, narzeka:

typ "RatingControl" nie ma członka "ratingButtonTapped"

Mam w 100% upewnił się, że funkcja istnieje w klasie i jest odpowiednio nazwana. Full source

Czy jest coś oczywistego, czego mi brakuje?

Co próbowałem:

  • Dodane @objc do definicji klasy, jak na this answer (ale wydaje dziwne dla Swift-jedyną rzeczą, nie?)

  • Wykonane ratingButtonTapped() wyraźnie public (ale to nie wygląda na to, że powinno być konieczne)

  • Fiddled wokół z ciągów zamiast wybieraka s, button.addTarget(self, action: "RatingControl.ratingButtonTapped", for: .touchDown) i wiele innych, ale to powoduje awarię później.

+3

A-ha! Funkcja automatycznego podpowiedzi XCode'a odpowiedziała na pytanie: Muszę teraz użyć 'RatingControl.ratingButtonTapped', bez nawiasów. To działa dobrze i kompiluje się teraz. Mógłbym usunąć to pytanie - ale jeśli ktoś chciałby udzielić odpowiedzi wyjaśniającej to bardziej szczegółowo i * dlaczego * tak się dzieje, moglibyśmy uczynić go użytecznym zasobem dla przyszłych czytelników mających ten sam problem? –

+2

Myślę, że pytanie jest w porządku, o ile jest oznaczane jako "swift3", ponieważ prawdopodobnie spowoduje to pewne zamieszanie, gdy tylko Swift 3 stanie się standardem. Jedna wskazówka dotycząca selektorów: jeśli metoda znajduje się w tym samym zakresie co wywołujący, możesz wywołać 'self.methodName' zamiast' ClassName.methodName', ale tylko detal kosmetyczny. – xoudini

+0

@xini dzięki! –

Odpowiedz

11

Stało się tak dlatego Swift 3 zmienił sposób, w jaki obsługuje imię parametru. W Swift 3 wszystkie nazwy parametrów muszą być używane podczas wywoływania funkcji, chyba że jako nazwa parametru została zadeklarowana jawna nazwa _.

Co używany jako selektor było w porządku, jeśli zadeklarował swoją funkcję jako:

func ratingButtonTapped(_ button: AnyObject) { 
    print("Button pressed ") 
} 

Można też wykorzystali to jako selektora:

#selector(RatingControl.ratingButtonTapped(button:)) 

Dodane @objc do definicji klasy zgodnie z tą odpowiedzią (ale wydaje się, że jest to dziwne z powodu Swift, nie?)

Twój kod może znajdować się w Swift, ale interakcja z środowiskiem Objective-C podczas kodowania dla Cocoa Touch (framework iOS). Selektor jest funkcją, która musi być widoczna dla środowiska wykonawczego Objective-C. Dostajesz to za darmo przez większość czasu, ponieważ implementujesz to w klasie, która ostatecznie dziedziczy po NSObject (jak UIViewController). Jeśli masz klasę tylko Swift, która nie dziedziczy po NSObject, możesz dodać @objc, aby klasa i metody były widoczne dla środowiska wykonawczego Objective-C.

15

W Swift 3 numer referencyjny metody dla: func ratingButtonTapped(button: UIButton) zmienia się na ratingButtonTapped(button:).

Tak więc, używając #selector(RatingControl.ratingButtonTapped(button:)) również działa.

A jeśli chcesz zachować #selector(RatingControl.ratingButtonTapped(_:)), to musisz zadeklarować metodę ratingButtonTapped jak:

func ratingButtonTapped(_ button: UIButton) { //<- `_` 
    print("Button pressed ") 
} 

A jeśli masz tylko jeden ratingButtonTapped metody w klasie, można zająć się selektor jako #selector(RatingControl.ratingButtonTapped) lub po prostu (od wewnątrz klasy RatingControl) #selector(ratingButtonTapped).

+0

po prostu świetna odpowiedź, dziękuje – manukv

+0

co, jeśli funkcja nie ma żadnych argumentów i tak się dzieje? – kashish

+0

@ chashish, nie jest jasne, jaki jest twój problem. Lepiej rozpocznij własną wątek, a otrzymasz lub poprowadzisz do bardziej odpowiednich odpowiedzi. – OOPer

3

Jeśli chcesz wywołać akcję, która jest w twoim kontrolerze widoku z innej klasy, możesz spróbować tego.

Użyj ViewController() dla swojego celu. Użyj ViewController.functionName dla selektora. Nie używaj metody pomocniczej dla zmiennej kontrolera widoku, takiej jak "vc", w przeciwnym razie nie będzie można uzyskać dostępu do obiektów w ViewController.

Oto przykład target:

self.addTarget(ViewController(), action:#selector(ViewController.Test(_:)), for: UIControlEvents.touchDragInside) 

w widoku kontrolera, tutaj jest przykładem działania

@IBAction func Test(_ sender: Any?) { 
    print("Goodtime was here") 

}

W celu należy dodać(), ale nie w selektor akcji. Nie musisz wywoływać @IBAction, może to być po prostu func. Niektórzy używają @objc lub public, każdy z tych prefiksów na akcji powinien działać.

Przejrzyj, jeśli akcja jest w innej klasie lub ViewController, musisz umieścić odwołanie do klasy zarówno w selektorze docelowym, jak i akcji. W przeciwnym razie spróbuje zawsze wywołać akcję w tym samym pliku, niezależnie od tego, czy jest poprawna w selektorze.Podobnie, jeśli akcja jest w tym samym użyciu pliku, self dla celu i wewnątrz selektora akcji.

Cheers

2

podczas dodawania cele przycisk 3 w szybkim najpierw trzeba być w class.

class SomeClass { 

    // ... 

    let button = UIButton() 
    // configure button to taste... 

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

    // ... 

    @objc public func doSomething() { 
    print("hello, world!") 
    } 

    // ... 

}