Jak dodać zdarzenie dotykowe do UIView?

280

Jak dodać zdarzenie dotykowe do UIView?
Próbuję:

UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, nextY)] autorelease];
[headerView addTarget:self action:@selector(myEvent:) forControlEvents:UIControlEventTouchDown];
// ERROR MESSAGE: UIView may not respond to '-addTarget:action:forControlEvents:'

Nie chcę tworzyć podklasy i nadpisywać

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Manni
źródło
Dla szybkiego możesz go uzyskać tutaj: stackoverflow.com/questions/28675209/…
Mr.Javed Multani

Odpowiedzi:

578

W iOS 3.2 i nowszym możesz używać rozpoznawania gestów. Na przykład w ten sposób poradzisz sobie ze zdarzeniem z kranu:

//The setup code (in viewDidLoad in your view controller)
UITapGestureRecognizer *singleFingerTap = 
  [[UITapGestureRecognizer alloc] initWithTarget:self 
                                          action:@selector(handleSingleTap:)];
[self.view addGestureRecognizer:singleFingerTap];

//The event handling method
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
  CGPoint location = [recognizer locationInView:[recognizer.view superview]];

  //Do stuff here...
}

Istnieje również kilka wbudowanych gestów. Sprawdź dokumenty dotyczące obsługi zdarzeń na iOS i UIGestureRecognizer. Mam również kilka przykładowych kodów na github, które mogą pomóc.

Nathan Eror
źródło
Ale to jest nadpisywanie defensywnej akcji widoku. ¿Czy można wywołać domyślną akcję dotykową widoku? Coś w stylu [super touchesBegan]? ...
M Penades,
2
Co robi linia CGPoint?
zakdances
2
@twojafriendzak CGPointreprezentuje lokalizację kranu w widoku podglądu podsłuchu. Za pomocą tego punktu można przenieść widok stuknięty (lub widok rodzeństwa) do lokalizacji stukniętej. Jest to bardziej przydatne w module obsługi UIPanGestureRecognizerdo przeciągania widoku po ekranie.
Nathan Eror
5
świetna zwięzła odpowiedź, dzięki. ale sheesh, czy nie byłoby miło, gdyby to było trochę łatwiejsze ?! :)
natbro
Nie jest tak źle, ale chciałbym, aby istniał interfejs oparty na blokach, taki jak github.com/neror/ftutils/blob/master/Headers/FTUtils/… . Xcode 4 obsługuje także dodawanie / konfigurowanie rozpoznawania gestów w Konstruktorze interfejsów.
Nathan Eror
126

Rozpoznawanie gestów

Istnieje wiele często używanych zdarzeń dotykowych (lub gestów), o których możesz zostać powiadomiony, gdy dodasz do widoku Gesture Recognizer . Domyślnie obsługiwane są następujące typy gestów:

  • UITapGestureRecognizer Stuknij (dotykając ekranu raz lub więcej razy)
  • UILongPressGestureRecognizer Długi dotyk (dotykanie ekranu przez długi czas)
  • UIPanGestureRecognizer Pan (przesuwając palcem po ekranie)
  • UISwipeGestureRecognizer Przesuń (szybko przesuwając palec)
  • UIPinchGestureRecognizer Zsuń palce ( rozsuń lub rozsuń dwa palce - zwykle w celu powiększenia)
  • UIRotationGestureRecognizer Obracanie (poruszanie dwoma palcami w kierunku kołowym)

Oprócz tego możesz także stworzyć własny rozpoznawanie gestów.

Dodawanie gestu w Konstruktorze interfejsów

Przeciągnij rozpoznawanie gestów z biblioteki obiektów do swojego widoku.

wprowadź opis zdjęcia tutaj

Kontroluj przeciąganie z gestu w konspekcie dokumentu do kodu kontrolera widoku, aby utworzyć ujście i akcję.

wprowadź opis zdjęcia tutaj

Powinno to być ustawione domyślnie, ale należy również upewnić się, że opcja User Action Enabled jest ustawiona na true dla twojego widoku.

wprowadź opis zdjęcia tutaj

Programowe dodawanie gestu

Aby programowo dodać gest, (1) utworzysz rozpoznawanie gestów, (2) dodasz go do widoku i (3) stworzysz metodę wywoływaną po rozpoznaniu gestu.

import UIKit
class ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 1. create a gesture recognizer (tap gesture)
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
        
        // 2. add the gesture recognizer to a view
        myView.addGestureRecognizer(tapGesture)
    }
    
    // 3. this method is called when a tap is recognized
    @objc func handleTap(sender: UITapGestureRecognizer) {
        print("tap")
    }
}

Notatki

  • Ten senderparametr jest opcjonalny. Jeśli nie potrzebujesz odniesienia do gestu, możesz go pominąć. Jeśli to zrobisz, usuń (sender:)nazwę metody po akcji.
  • Nazewnictwo handleTapmetody było dowolne. Nazwij go, jak chcesz .action: #selector(someMethodName(sender:))

Więcej przykładów

Możesz przestudiować rozpoznawanie gestów, które dodałem do tych widoków, aby zobaczyć, jak działają.

wprowadź opis zdjęcia tutaj

Oto kod tego projektu:

import UIKit
class ViewController: UIViewController {
    
    @IBOutlet weak var tapView: UIView!
    @IBOutlet weak var doubleTapView: UIView!
    @IBOutlet weak var longPressView: UIView!
    @IBOutlet weak var panView: UIView!
    @IBOutlet weak var swipeView: UIView!
    @IBOutlet weak var pinchView: UIView!
    @IBOutlet weak var rotateView: UIView!
    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Tap
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        tapView.addGestureRecognizer(tapGesture)
        
        // Double Tap
        let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
        doubleTapGesture.numberOfTapsRequired = 2
        doubleTapView.addGestureRecognizer(doubleTapGesture)
        
        // Long Press
        let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(gesture:)))
        longPressView.addGestureRecognizer(longPressGesture)
        
        // Pan
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(gesture:)))
        panView.addGestureRecognizer(panGesture)
        
        // Swipe (right and left)
        let swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(gesture:)))
        let swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(gesture:)))
        swipeRightGesture.direction = UISwipeGestureRecognizerDirection.right
        swipeLeftGesture.direction = UISwipeGestureRecognizerDirection.left
        swipeView.addGestureRecognizer(swipeRightGesture)
        swipeView.addGestureRecognizer(swipeLeftGesture)
        
        // Pinch
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(gesture:)))
        pinchView.addGestureRecognizer(pinchGesture)
        
        // Rotate
        let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotate(gesture:)))
        rotateView.addGestureRecognizer(rotateGesture)
        
    }
    
    // Tap action
    @objc func handleTap() {
        label.text = "Tap recognized"
        
        // example task: change background color
        if tapView.backgroundColor == UIColor.blue {
            tapView.backgroundColor = UIColor.red
        } else {
            tapView.backgroundColor = UIColor.blue
        }
        
    }
    
    // Double tap action
    @objc func handleDoubleTap() {
        label.text = "Double tap recognized"
        
        // example task: change background color
        if doubleTapView.backgroundColor == UIColor.yellow {
            doubleTapView.backgroundColor = UIColor.green
        } else {
            doubleTapView.backgroundColor = UIColor.yellow
        }
    }
    
    // Long press action
    @objc func handleLongPress(gesture: UILongPressGestureRecognizer) {
        label.text = "Long press recognized"
        
        // example task: show an alert
        if gesture.state == UIGestureRecognizerState.began {
            let alert = UIAlertController(title: "Long Press", message: "Can I help you?", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
    }
    
    // Pan action
    @objc func handlePan(gesture: UIPanGestureRecognizer) {
        label.text = "Pan recognized"
        
        // example task: drag view
        let location = gesture.location(in: view) // root view
        panView.center = location
    }
    
    // Swipe action
    @objc func handleSwipe(gesture: UISwipeGestureRecognizer) {
        label.text = "Swipe recognized"
        
        // example task: animate view off screen
        let originalLocation = swipeView.center
        if gesture.direction == UISwipeGestureRecognizerDirection.right {
            UIView.animate(withDuration: 0.5, animations: {
                self.swipeView.center.x += self.view.bounds.width
            }, completion: { (value: Bool) in
                self.swipeView.center = originalLocation
            })
        } else if gesture.direction == UISwipeGestureRecognizerDirection.left {
            UIView.animate(withDuration: 0.5, animations: {
                self.swipeView.center.x -= self.view.bounds.width
            }, completion: { (value: Bool) in
                self.swipeView.center = originalLocation
            })
        }
    }
    
    // Pinch action
    @objc func handlePinch(gesture: UIPinchGestureRecognizer) {
        label.text = "Pinch recognized"
        
        if gesture.state == UIGestureRecognizerState.changed {
            let transform = CGAffineTransform(scaleX: gesture.scale, y: gesture.scale)
            pinchView.transform = transform
        }
    }
    
    // Rotate action
    @objc func handleRotate(gesture: UIRotationGestureRecognizer) {
        label.text = "Rotate recognized"
        
        if gesture.state == UIGestureRecognizerState.changed {
            let transform = CGAffineTransform(rotationAngle: gesture.rotation)
            rotateView.transform = transform
        }
    }
}

Notatki

  • Możesz dodać wiele rozpoznawania gestów do jednego widoku. Jednak dla uproszczenia tego nie zrobiłem (oprócz gestu machnięcia). Jeśli potrzebujesz w swoim projekcie, powinieneś przeczytać dokumentację rozpoznawania gestów . Jest to dość zrozumiałe i pomocne.
  • Znane problemy z moimi przykładami powyżej: (1) Widok panoramy resetuje ramkę przy zdarzeniu następnego gestu. (2) Widok przesunięcia pochodzi z niewłaściwego kierunku przy pierwszym przesunięciu. (Te błędy w moich przykładach nie powinny jednak wpływać na twoje zrozumienie działania Gogures Recognizers.)
Suragch
źródło
2
Twoja odpowiedź jest naprawdę miła i pomocna ze szczegółowym opisem. I dlatego mam głos, na który też odpowiesz. Ale właśnie przegapiłeś jedną instrukcję, którą, jak sądzę, musisz uwzględnić w swojej odpowiedzi, ponieważ wyjaśniłeś ją szczegółowo. Ta instrukcja dotyczy ustawienia „<yourView> .EnableUserInteraction = TRUE”. Mam nadzieję, że się ze mną zgadzasz.
Er. Vihar
3
Aby to zadziałało UIView, musisz również dodać:self.isUserInteractionEnabled = true;
neiker
52

Myślę, że możesz po prostu użyć

UIControl *headerView = ...
[headerView addTarget:self action:@selector(myEvent:) forControlEvents:UIControlEventTouchDown];

mam na myśli headerView rozszerza się z UIControl.

LiCheng
źródło
7
To lepsza odpowiedź. UITapGestureRecognizernie zastępuje UIControlEventTouchDown. TapZazwyczaj komponuje UIControlEventTouchDowni UIControlEventTouchUpInside.
ohho,
62
A UIViewnie jest a, UIControla zatem nie ma dostępu do addTarget:action:forControlEvents:.
RobertJoseph
9
Zauważ również, że UIControl dziedziczy z UIView. Dla moich celów wszystko, co musiałem zrobić, to prosta zmiana typu podklasy.
precle1337
1
możesz ustawić jego klasę na UIControl @RobertJoseph .. Przejdź do pliku xib i ustaw klasę View Custom na UIControl. teraz możesz obsłużyć zdarzenie w nim ..
Zar E Ahmer
2
Dlaczego więc jest to lepsze rozwiązanie, skoro zmieniam dziedziczenie tylko po to, by obsługiwać krany?
Anthony Glyadchenko
21

Swift 3 i Swift 4

import UIKit

extension UIView {
  func addTapGesture(tapNumber: Int, target: Any, action: Selector) {
    let tap = UITapGestureRecognizer(target: target, action: action)
    tap.numberOfTapsRequired = tapNumber
    addGestureRecognizer(tap)
    isUserInteractionEnabled = true
  }
}

Posługiwać się

yourView.addTapGesture(tapNumber: 1, target: self, action: #selector(yourMethod))
Indrajit Sinh Rayjada
źródło
Dzięki. Działa świetnie!
Mikrasya
1
Kocham to! Dodanie do wszystkich projektów;)
budidino
17

Na podstawie zaakceptowanej odpowiedzi możesz zdefiniować makro:

#define handle_tap(view, delegate, selector) do {\
    view.userInteractionEnabled = YES;\
    [view addGestureRecognizer: [[UITapGestureRecognizer alloc] initWithTarget:delegate action:selector]];\
} while(0)

To makro używa ARC, więc nie ma release połączenia.

Przykład użycia makra:

handle_tap(userpic, self, @selector(onTapUserpic:));
rudyryk
źródło
4
Jeśli utworzysz widok w serii ujęć, nie zapomnij włączyć opcji „włączona interakcja użytkownika”.
David
gdzie powinniśmy zdefiniować makra w .h lub .m i jaka powinna być nazwa parametrów. mam na myśli #define handle_tap (widok UIView, delegat delegacji, selektor SEL) do ..
Zar E Ahmer,
8

Możesz to osiągnąć, dodając do kodu Gogure Recogniser.

Krok 1: ViewController.m:

// Declare the Gesture.
UITapGestureRecognizer *gesRecognizer = [[UITapGestureRecognizer alloc] 
                                          initWithTarget:self 
                                          action:@selector(handleTap:)];
gesRecognizer.delegate = self;

// Add Gesture to your view.
[yourView addGestureRecognizer:gesRecognizer]; 

Krok 2: ViewController.m:

// Declare the Gesture Recogniser handler method.
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer{
   NSLog(@"Tapped");
}

UWAGA: tutaj Twój widok w moim przypadku był @property (strong, nonatomic) IBOutlet UIView *localView;

EDYCJA: * localView to białe pole w Main.storyboard od dołu

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Annu
źródło
Mam wiele widoków, więc czy mogę podać identyfikator nadawcy?
Moin Shirazi
Musisz przejść do wewnętrznego widoku podrzędnego, a następnie możesz ustawić nadawcę .--------- dla (widok UIView * w self.topicScrollView.subviews) {// Przejdź do podglądu, jeśli ([widok to KindOfClass: [Klasa UIButton]]) {// Przejdź do tego konkretnego widoku // Tutaj dotarłeś do tego punktu. }}
Annu
8

W Swift 4.2 i Xcode 10

Użyj UITapGestureRecognizer, aby dodać zdarzenie dotykowe

//Add tap gesture to your view
let tap = UITapGestureRecognizer(target: self, action: #selector(handleGesture))
yourView.addGestureRecognizer(tap)

// GestureRecognizer
@objc func handleGesture(gesture: UITapGestureRecognizer) -> Void {
//Write your code here
}

Jeśli chcesz użyć SharedClass

//This is my shared class
import UIKit

class SharedClass: NSObject {

    static let sharedInstance = SharedClass()

    //Tap gesture function
    func addTapGesture(view: UIView, target: Any, action: Selector) {
        let tap = UITapGestureRecognizer(target: target, action: action)
        view.addGestureRecognizer(tap)
    }
} 

Mam 3 widoki w moim ViewController o nazwie view1, view2 i view3.

override func viewDidLoad() {
    super.viewDidLoad()
    //Add gestures to your views
    SharedClass.sharedInstance.addTapGesture(view: view1, target: self, action: #selector(handleGesture))
    SharedClass.sharedInstance.addTapGesture(view: view2, target: self, action: #selector(handleGesture))
    SharedClass.sharedInstance.addTapGesture(view: view3, target: self, action: #selector(handleGesture2))

}

// GestureRecognizer
@objc func handleGesture(gesture: UITapGestureRecognizer) -> Void {
    print("printed 1&2...")
}
// GestureRecognizer
@objc func handleGesture2(gesture: UITapGestureRecognizer) -> Void {
    print("printed3...")
}
iOS
źródło
6

Oto wersja Swift:

// MARK: Gesture Extensions
extension UIView {

    func addTapGesture(#tapNumber: Int, target: AnyObject, action: Selector) {
        let tap = UITapGestureRecognizer (target: target, action: action)
        tap.numberOfTapsRequired = tapNumber
        addGestureRecognizer(tap)
        userInteractionEnabled = true
    }

    func addTapGesture(#tapNumber: Int, action: ((UITapGestureRecognizer)->())?) {
        let tap = BlockTap (tapCount: tapNumber, fingerCount: 1, action: action)
        addGestureRecognizer(tap)
        userInteractionEnabled = true
    }
}
Esqarrouth
źródło
co jest BlockTap?
He Yifei 非 一 非
1
O rany, zapomniałem tej części. Jest to funkcja niestandardowa. Tutaj: github.com/goktugyil/EZSwiftExtensions/blob/master/Sources/…
Esqarrouth
5

Swift 3:

let tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGestureRecognizer(_:)))
view.addGestureRecognizer(tapGestureRecognizer)

func handleTapGestureRecognizer(_ gestureRecognizer: UITapGestureRecognizer) {

}
Giang
źródło
1

W dzisiejszych czasach wydaje się to dość proste. To jest wersja Swift.

let tap = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
    view.addGestureRecognizer(tap)

@objc func viewTapped(recognizer: UIGestureRecognizer)
{
    //Do what you need to do!
}
daj mi spokój
źródło
0

Oto iost tapureure; Najpierw musisz utworzyć akcję dla GestureRecognizer po napisaniu poniższego kodu pod akcją, jak pokazano poniżej

- (IBAction)tapgesture:(id)sender

{


[_password resignFirstResponder];


[_username resignFirstResponder];

NSLog(@" TapGestureRecognizer  tapped");

}
satycki
źródło
0

Innym sposobem jest dodanie przezroczystego przycisku do widoku

UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
b.frame = CGRectMake(0, 0, headerView.width, headerView.height);
[headerView addSubview:b];
[b addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchDown];

A następnie kliknij przycisk obsługi:

- (void)buttonClicked:(id)sender
{}
thanhbinh84
źródło
0

Utwórz funkcję rozpoznawania gestów (podklasę), która będzie realizować zdarzenia dotykowe, takie jak touchesBegan . Następnie możesz dodać go do widoku.

W ten sposób użyjesz kompozycji zamiast podklasy (co było żądaniem).

Geri Borbás
źródło
0

Dlaczego nie spróbujecie SSEventListener ?

Nie musisz tworzyć żadnego rozpoznawania gestów i oddzielać logikę od innej metody. SSEventListenerobsługuje ustawianie bloków detektora w widoku, aby nasłuchiwać gestu pojedynczego stuknięcia, gestu dwukrotnego stuknięcia i gestu stuknięcia w N, jeśli chcesz, oraz gestu długiego naciśnięcia. Ustawienie nasłuchiwania gestu pojedynczego dotknięcia staje się w ten sposób:

[view ss_addTapViewEventListener:^(UITapGestureRecognizer *recognizer) { ... } numberOfTapsRequired:1];

Shengsheng
źródło
0

Cel C:

UIControl *headerView = [[UIControl alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, nextY)];
[headerView addTarget:self action:@selector(myEvent:) forControlEvents:UIControlEventTouchDown];

Szybki:

let headerView = UIControl(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: nextY))
headerView.addTarget(self, action: #selector(myEvent(_:)), for: .touchDown)

Pytanie brzmi:

Jak dodać zdarzenie dotykowe do UIView?

To nie prosi o zdarzenie z kranu .

W szczególności OP chce wdrożyć UIControlEventTouchDown

Przełączanie UIViewsię UIControljest dobrym rozwiązaniem tutaj, bo Gesture Recognisersnie wiem nic na temat .touchDown, .touchUpInside, .touchUpOutsideitd.

Dodatkowo UIControl dziedziczy z UIView, więc nie tracisz żadnej funkcjonalności.

Jeśli wszystko, czego potrzebujesz, to dotknij, możesz użyć narzędzia do rozpoznawania gestów. Ale jeśli chcesz mieć lepszą kontrolę, tak jak to pytanie, potrzebujesz UIControl.

https://developer.apple.com/documentation/uikit/uicontrol?language=objc https://developer.apple.com/documentation/uikit/uigesturerecognizer?language=objc

janakmshah
źródło