Czy możesz dołączyć UIGestureRecognizer do wielu widoków?

228
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)];
[self.view1 addGestureRecognizer:tapGesture];
[self.view2 addGestureRecognizer:tapGesture];
[tapGesture release];

W powyższym kodzie view2są rozpoznawane tylko dotknięcia . Jeśli skomentuję trzecią linię, wówczas view1rozpoznawane są stuknięcia . Jeśli mam rację i możesz użyć rozpoznawania gestów tylko raz, nie jestem pewien, czy to błąd, czy też potrzebuje trochę dodatkowej dokumentacji.

kubi
źródło

Odpowiedzi:

334

A UIGestureRecognizernależy stosować z jednym widokiem. Zgadzam się, że dokumentacja jest nierówna. To UIGestureRecognizerma jedną viewwłaściwość, która daje:

widok

Widok, do którego dołączony jest moduł rozpoznawania gestów. (tylko czytać)

@property (nonatomic, readonly) UIView * widok

Dyskusja Dołączasz (lub dodajesz) moduł rozpoznawania gestów do obiektu UIView za pomocą metody addGestureRecognizer:.

TomSwift
źródło
11
Ponieważ dodanie rozpoznawania gestów do widoku odbywa się w czasie wykonywania (vs. czas kompilacji).
TomSwift,
1
Rozumiem, ale podobnie jak wykrywanie, które mówi, że nie użyliśmy zmiennej, XCode może powiedzieć na podstawie kodu, że przekazaliśmy ten sam moduł rozpoznawania do wielu widoków i może ostrzec kodera.
Zoltán Matók
1
Ostrzeżenie kompilatora o wielu widokach przypisujących ten sam UITapGestureRecognizer jest nonsensowne, ponieważ możesz chcieć to zrobić celowo, na przykład, jeśli chcesz przenieść rozpoznawanie gestów dotykowych z jednego widoku do drugiego. To powiedziawszy, to głupie ograniczenie, że rozpoznawania gestów nie można używać w wielu widokach.
Erik van der Neut
1
iOS 9 wymusza teraz pojedynczy widok na rozpoznawanie gestów, korzystałem z poniższej metody konstruktora interfejsów, ale teraz próbuję go użyć (pojawia się następujący komunikat (niektóre szczegóły wycięte dla zwięzłości)): OSTRZEŻENIE: Rozpoznawanie gestów (< UITapGestureRecognizer: .....>) został skonfigurowany w serii ujęć / xib, aby był dodawany do więcej niż jednego widoku (-> <UIView:; frame = (0 44; 600 536); autoresize = RM + BM; gestRecognizers = < NSArray ...:>; layer = <CALayer: ... >>) na raz, nigdy nie było to dozwolone, a teraz jest egzekwowane. Począwszy od iOS 9.0, zostanie umieszczony w pierwszym widoku, do którego został załadowany.
George Brown
Jeśli dodajesz do widoku za drugim razem, widok został dołączony wcześniej przez to rozpoznawanie staje się automatycznie odłączany Widok UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didPressed:)]; [self.view1 addGestureRecognizer:tapRecognizer]; [self.view2 addGestureRecognizer:tapRecognizer];wyjściowy 1 nie ma tablicy rozpoznawania gestów; view2 ma tablicę rozpoznawania gestów
kokos8998
48

Obejrzałem to, korzystając z poniższego.

for (UIButton *aButton in myButtons) {

            UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
            longPress.minimumPressDuration=1.0;
            [aButton addGestureRecognizer:longPress];
            [longPress release];

}

Następnie w metodzie handleLongPress po prostu ustawiam przycisk UIB równy widokowi rozpoznawania gestów i rozgałęziam to, co robię na podstawie tego przycisku

- (void)handleLongPress:(UILongPressGestureRecognizer*)gesture {
    if ( gesture.state == UIGestureRecognizerStateEnded ) {
        UIButton *whichButton=(UIButton *)[gesture view];
        selectedButton=(UIButton *)[gesture view];
    ....
}
kwalker
źródło
1
Świetna odpowiedź. Dzięki za tonę. To mogłaby być zaakceptowana odpowiedź, gdyby pytanie brzmiało: „Jak dołączyć UIGestureRecognizer do wielu widoków?”
D_D
7
To (lub coś bardzo zbliżonego do tego) nie działało dla mnie. Dodałem kilka widoków do rozpoznawania gestów dotknij w Konstruktorze interfejsów i podłączyłem rozpoznawanie do akcji. Akcja była wywoływana za każdym razem, gdy dołączony widok został dotknięty, ale gest.view był zawsze ostatnim dołączonym widokiem.
Aneil Mallavarapu
To jest naprawdę fajna odpowiedź, a także bardzo pomocna i zgodna z @MicRO +1
Dilip
2
Aneil, to dlatego, że nie utworzyłeś nowych instancji rozpoznawania gestów. Pętla w tej odpowiedzi polega na tym, że tworzone są nowe instancje rozpoznawania gestów, z których każdy ma tylko jeden widok. Wszystkie mogą wskazywać ten sam moduł obsługi, gdzie następnie sprawdzasz widok, aby zobaczyć, który został dotknięty.
Erik van der Neut
1
Czy ktoś może potwierdzić, że nie działa to już w bieżącej wersji Obj-C / Swift?
Maxi Mus
18

W przypadku Swift 3, jeśli ktoś tego wymaga: na podstawie Bhavik Rathod Odpowiedź powyżej.

 func setGestureRecognizer() -> UIPanGestureRecognizer {

        var panRecognizer = UIPanGestureRecognizer()

        panRecognizer = UIPanGestureRecognizer (target: self, action: #selector(pan(panGesture:)))
        panRecognizer.minimumNumberOfTouches = 1
        panRecognizer.maximumNumberOfTouches = 1
        return panRecognizer
    }

        ///set the recognize in multiple views
        view1.addGestureRecognizer(setGestureRecognizer())
        view2.addGestureRecognizer(setGestureRecognizer())
George Asda
źródło
3
to w zasadzie tworzenie wielu gestów dla dwóch widoków, wciąż ta sama zasada: każdy gest ma tylko jeden widok, do którego można dołączyć
Abdoelrhman
3
Nie, funkcja tworzy gest za każdym razem, gdy się nazywa
Abdoelrhman
2
nazwa funkcji jest niepoprawna. funkcją logiczną jest tutaj funkcja pobierania. dlatego należy ją nazwać: getGestureRecognizeponieważ to właśnie pełni ta funkcja
David Seek
Pracuj dla mnie dobrze! I kod jest bardziej przejrzysty niż tworzenie wielu zmiennych lub wstawianie całego kodu do tworzenia w addGestureRecognizer
Codenator81
11

Możemy zrobić coś takiego, to łatwe i proste

1) utwórz w kontrolerze funkcję jak poniżej (ta funkcja zwróci GestureRecognizer)

-(UITapGestureRecognizer*)setRecognizer{
     UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(openProfile)];
     [gestureRecognizer setNumberOfTapsRequired:1];
     return gestureRecognizer;
}

2) ustaw teraz ten moduł rozpoznawania w wielu widokach

[self.view1 addGestureRecognizer:[self setRecognizer]]; 
[self.view2 addGestureRecognizer:[self setRecognizer]];
Bhavik Rathod
źródło
Nie działa dla mnie, gdy używam dwóch etykiet zamiast widoków.
Mihir Oza
3
@Mihir Oza, nie może bezpośrednio działać dla UILabels. Ponieważ etykiety nie mają sensu dla interakcji użytkownika. Jeśli chcesz dodać gesty dla UILabels, dodaj tę pojedynczą linię labelName..isUserInteractionEnabled = true w Swift. Następnie dodaj gesty.
iOS
Jest już za późno, już to naprawiłem. Ale dzięki za sugestię. Twój komentarz będzie pomocny dla użytkowników stosu. Doceniany!
Mihir Oza
1
Chyba linia setNumberOfTapsRequired:1nie jest konieczna
Naveed Abbas,
9

Nie, nie należy dołączać rozpoznawania gestów do więcej niż jednego widoku.

Dokumentacja Apple zawiera te wyraźne informacje:

Urządzenia rozpoznające gesty są dołączone do widoku

Każdy moduł rozpoznawania gestów jest powiązany z jednym widokiem. Natomiast widok może zawierać wiele rozpoznawania gestów, ponieważ pojedynczy widok może reagować na wiele różnych gestów. Aby rozpoznawanie gestów rozpoznawało dotknięcia występujące w danym widoku, musisz dołączyć rozpoznawanie gestów do tego widoku.

Podręcznik obsługi zdarzeń dla systemu iOS - Gogure Recognizers Apple Developer Library

Podczas gdy, jak wspominają inni, mogą one działać w niektórych przypadkach, jest to wyraźnie sprzeczne z dokumentacją i może ulec zmianie w każdej przyszłej wersji iOS.

Możesz dodać osobne rozpoznawanie gestów do widoków, które chcesz monitorować, i mogą one dzielić wspólną akcję.

Joseph Lord
źródło
4

Cóż, jeśli ktoś nie chce kodować dodawania widoku gestów dla wielu przycisków, takich jak kwalker odpowiedział powyżej, i chce to zrobić za pomocą Konstruktora interfejsów, może to ci pomóc.

1) Możesz dodać Rozpoznawanie gestów Długie naciśnięcie z Biblioteki obiektów, tak jak dodajesz inne obiekty, takie jak UIButtons i UILabels.

wprowadź opis zdjęcia tutaj Początkowo użyłem tylko jednego

2) Ustaw odnośniki do UIButtoni wysyłaj akcje z właścicielem pliku.

wprowadź opis zdjęcia tutaj

Uwaga: Jeśli masz wiele przycisków UIB lub dowolnego innego obiektu, będziesz potrzebować osobnego rozpoznawania gestów dla każdego z nich. Aby uzyskać więcej informacji, zapoznaj się z moim pytaniem. Nieprawidłowy znacznik UIButton w rozpoznawaniu gestów Długie naciśnięcie

rohan-patel
źródło
Bardzo łatwo jest powiązać więcej niż jeden UIView z systemem rozpoznawania gości za pomocą IB. Pytanie dotyczyło generowania kodu.
AlexeyVMP
3

jeśli masz stały widok, sugeruję zrobienie czegoś takiego

[self.view1 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)]];
[self.view2 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)]];

w ten sposób zmniejszy się wiele różnych bezużytecznych zmiennych

Raynaldio Limarga
źródło
3

Możesz utworzyć ogólne rozszerzenie, aby łatwo dodawać rozpoznawanie gestów. To tylko przykład, ale może tak wyglądać

extension UIView {

    func setGestureRecognizer<Gesture: UIGestureRecognizer>(of type: Gesture.Type, target: Any, actionSelector: Selector, swipeDirection: UISwipeGestureRecognizer.Direction? = nil, numOfTaps: Int = 1) {
    let getRecognizer = type.init(target: target, action: actionSelector)

    switch getRecognizer {
    case let swipeGesture as UISwipeGestureRecognizer:
        guard let direction = swipeDirection else { return }
        swipeGesture.direction = direction
        self.addGestureRecognizer(swipeGesture)
    case let tapGesture as UITapGestureRecognizer:
        tapGesture.numberOfTapsRequired = numOfTaps
        self.addGestureRecognizer(tapGesture)
    default:
        self.addGestureRecognizer(getRecognizer)
    }
  }

}

Aby dodać rozpoznawanie 2 tapów w widoku, do którego chcesz zadzwonić:

let actionSelector = #selector(actionToExecute)
view.setGestureRecognizer(of: UITapGestureRecognizer.self, target: self, actionSelector: actionSelector, numOfTaps: 2)

Możesz także łatwo dodać rozpoznawanie przeciągnięć

view.setGestureRecognizer(of: UISwipeGestureRecognizer.self, target: self, actionSelector: actionSelector, swipeDirection: .down)

i tak dalej. Pamiętaj tylko, że cel musi być powiązany z selektorem.

Jaskółka oknówka
źródło
2

Zastąp klasę przez „ <UIScrollViewDelegate>

I użyj tej metody w klasie .m:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

Ta metoda pomoże Ci włączyć wielokrotne przesunięcie w jednym widoku.

AnkitRox
źródło
2

Co powiesz na ponowne zapisanie (odtworzenie) swojego GestureRecogni za każdym razem, gdy dodasz rozpoznawanie gestów wskazujące na ten sam func. W poniższym przypadku to działa. Korzystam z IBOutletCollection

Swift 2:

@IBOutlet var topicView: [UIView]!

override func viewDidLoad() {
        for view in self.topicView as [UIView] {
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "viewClicked:"))
    }
}

func viewClicked(recognizer: UITapGestureRecognizer) {
    print("tap")
}
Febaisi
źródło
-6

Możesz to zrobić za pomocą tego kodu moje widoki, które są widokami obrazów w xib.

- (void)viewDidLoad
{
    firstIV.tag = 501;
    secondIV.tag = 502;
    thirdIV.tag = 503;
    forthIV.tag = 504;

    [self addTapGesturetoImageView: firstIV];
    [self addTapGesturetoImageView: secondIV];
    [self addTapGesturetoImageView: thirdIV];
    [self addTapGesturetoImageView: forthIV];
}

-(void)addTapGesturetoImageView:(UIImageView*)iv
{
    iv.userInteractionEnabled = YES;
    UITapGestureRecognizer * textfielBGIVTapGasture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(textfielBGIVTapped:)];
    textfielBGIVTapGasture.numberOfTapsRequired = 1;
    [iv addGestureRecognizer:textfielBGIVTapGasture];
}

- (void)textfielBGIVTapped:(UITapGestureRecognizer *)recognizer {
    int tag = recognizer.view.tag-500;
    switch (tag) {
        case 1:
        {
            //firstIV tapped;
            break;
        }
        case 2:
        {
            //secondIV tapped;
            break;
        }
        case 3:
        {
            //thirdIV tapped;
            break;
        }
        case 4:
        {
            //forthIV tapped;
            break;
        }
        default: {
            break;
        }
    }
}
Dilip
źródło
1
Tworzysz wiele rozpoznawania gestów; moje pierwotne pytanie dotyczyło ponownego użycia rozpoznawania jednego gestu, czego nie można zrobić.
kubi
1
Jaki jest sens dodawania 500do wszystkich tagów widoków, a następnie odejmowania 500? Dlaczego po prostu nie zacząć tagów 1(a nawet 0) zamiast 501?
ma11hew28,
@MattDiPasquale, nie ma znaczenia, czy chcesz zacząć od 1jego, właśnie skopiowałem ten kod z mojej aplikacji, z której go otrzymuję 501. Ale tak, nie dawaj 0bcoz, gdzieś czytałem, że zawsze wskazuje to na widok rodzica, więc stworzy to komplikację, uwierz mi, że się z tym zmierzyłem.
Dilip
Kluczowym tekstem w dokumentacji jest „Widok zawiera silne odniesienie do rozpoznawania gestów”. co oznacza, że ​​widok jest właścicielem gestu. Gest może mieć tylko jednego właściciela. Zobacz link
Phantom59,