UIGestureRecognizer, dokunma olaylarını işlemek için alt görünümü engeller


82

Bunun nasıl doğru şekilde yapıldığını anlamaya çalışıyorum . Durumu tasvir etmeye çalıştım: görüntü açıklamasını buraya girin

Bir alt görünümü UITableViewolarak a ekliyorum UIView. UIViewBir kademe ve yanıt verir pinchGestureRecognizer, ama bunu yaparken, tableview bu iki hareketleri için reaksiyona durdurur (hala kaydırma hareketiyle tepki).

Aşağıdaki kodla çalışmasını sağladım, ancak bu kesinlikle iyi bir çözüm değil ve eminim daha iyi bir yol vardır. Bu, UIView(süpervizör) içine konur :

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if([super hitTest:point withEvent:event] == self) {
        for (id gesture in self.gestureRecognizers) {
            [gesture setEnabled:YES];
        }
        return self;
    }
    for (id gesture in self.gestureRecognizers) {
        [gesture setEnabled:NO];
    }
    return [self.subviews lastObject];
}

Yanıtlar:


182

Çok benzer bir sorun yaşadım ve çözümümü bu SO sorusunda buldum . Özetle, kendinizi temsilci olarak ayarlayın UIGestureRecognizerve ardından tanıyıcınızın dokunuşu işlemesine izin vermeden önce hedeflenen görünümü kontrol edin. İlgili temsilci yöntemi şudur:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch

3
Bu çözüm gibi bunun gibi en dokunuşlar ile karıştırmasını dahil, gelmez hitTest:withEvent:ya pointInside:withEvent:.
DarkDust

6
Temiz çözüm, örneğin return !(touch.view == givenView);belirli bir görünümü hariç tutmak istiyorsanız veya return !(touch.view.tag == kTagNumReservedForExcludingViews);tanıyıcınızın bir grup farklı alt görünümde dokunmayı işlemesini durdurmak istediğinizde test edebileceğiniz gibi.
Cate

6
Vuruş testini ile yapardım - (BOOL)isDescendantOfView:(UIView *)view. Bu, - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)eventUIGestureRecognizer'ın alt sınıflarını oluştururken de iyi çalışır .
Christoph

1
@Justin, ya hareket tanıyıcımız uiscrollview'e aitse ve hareket tanıyıcıları yetkilendiremiyorsak?
Gökhan Barış Aker

109

Dokunma olaylarının alt görünümlere engellenmesi varsayılan davranıştır. Bu davranışı değiştirebilirsiniz:

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)];
r.cancelsTouchesInView = NO;
[agentPicker addGestureRecognizer:r];

5
Bu, dokunma olaylarını alt görünümlere gönderecek, ancak aynı zamanda hareket tanıyıcıya da gönderilecektir. Bu, alt görünümlerin engellenmesini önleyecek, ancak jest tanıyıcı yine de alt görünümlerde tanınacaktır.
Jonathan.

@Jonathan. Evet ben size katılıyorum. Hareket işleyici yöntemimde yaptığım şey, ilgili alt görünümde hareket konumunun olup olmadığını kontrol etmekti, bu durumda kodun geri kalanını yürütmem gerekmiyor. Ayrıca, bu geçici çözümü seçmemin bir nedeni, UITapGestureRecognizer'ın translationInViewyöntemi bildirmemesidir . Bu nedenle, yukarıda bahsedilen UIGestureRecognizerDelegate yönteminin uygulanması yalnızca hata ile çökmeye neden olur ... unrecognized selector sent to blah. Gibi kullanım şey kontrol etmek için: CGRectContainsPoint(subview.bounds, [recognizer locationInView:subview]).
MkVal

OP'nin sorusuna göre bu cevap ana cevap olarak seçilmelidir.
farzadshbfn

5

Kendi tablo görünümüne sahip bir açılır alt görünüm görüntülüyordum. Sonuç olarak, touch.viewbazen UITableViewCell. Düşündüğüm alt sınıf olduğundan emin olmak için süper sınıf (lar) dan geçmek zorunda kaldım:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    UIView *view = touch.view;
    while (view.class != UIView.class) {
        // Check if superclass is of type dropdown
        if (view.class == dropDown.class) { // dropDown is an ivar; replace with your own
            NSLog(@"Is of type dropdown; returning NO");
            return NO;
        } else {
            view = view.superview;
        }
    }

    return YES;
}

While döngüsü bunu
açıklıyor

5

@Pin Shih Wang cevabına dayalı olarak . Dokunma hareketi tanıyıcısını içeren görünümün dışındaki tüm dokunmaları yok sayıyoruz. Tüm tıklamalar, belirlediğimiz gibi normal şekilde görünüm hiyerarşisine yönlendirilir tapGestureRecognizer.cancelsTouchesInView = false. İşte Swift3 / 4'teki kod:

func ensureBackgroundTapDismissesKeyboard() {
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
    tapGestureRecognizer.cancelsTouchesInView = false
    self.view.addGestureRecognizer(tapGestureRecognizer)
}

@objc func handleTap(recognizer: UIGestureRecognizer) {
    let location = recognizer.location(in: self.view)
    let hitTestView = self.view.hitTest(location, with: UIEvent())
    if hitTestView?.gestureRecognizers?.contains(recognizer) == .some(true) {
        // I dismiss the keyboard on a tap on the scroll view
        // REPLACE with own logic
        self.view.endEditing(true)
    }
}

Hareket tanıyıcıyı a ile nasıl karşılaştırabilirim UISwipeActionStandardButton? Denedim.some(UISwipeActionStandardButton)
Jalil

4

Bir olasılık, hareket tanıyıcınızı alt sınıflara -touchesBegan:withEvent:ayırmaktır (henüz yapmadıysanız) ve her dokunmanın dışlanmış bir alt görünümde başlayıp başlamadığını ve başladıysa -ignoreTouch:forEvent:bu dokunuşu çağıracak şekilde geçersiz kılmaktır .

Açıkçası, hariç tutulan alt görünümü veya belki daha da iyisi bir dizi hariç tutulan alt görünümü takip etmek için bir özellik eklemeniz gerekir.


Burada bir yığın kod var github.com/…
johndpope

2

Herhangi bir sınıfı miras almadan yapmak mümkündür.

jestin geri arama seçicisinde jestRecognizer'ları kontrol edebilirsiniz

view.gestureRecognizers, jestRecognizer'ı içermiyorsa, yok sayın

Örneğin

- (void)viewDidLoad
{
    UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self     action:@selector(handleSingleTap:)];
    singleTapGesture.numberOfTapsRequired = 1;
}

view.gestureRecognizers'ı buradan kontrol edin

- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer
{
    UIEvent *event = [[UIEvent alloc] init];
    CGPoint location = [gestureRecognizer locationInView:self.view];

    //check actually view you hit via hitTest
    UIView *view = [self.view hitTest:location withEvent:event];

    if ([view.gestureRecognizers containsObject:gestureRecognizer]) {
        //your UIView
        //do something
    }
    else {
        //your UITableView or some thing else...
        //ignore
    }
}

Diğer yanıtlarda belirtildiği gibi, bu yöntemi kullanıyorsanız, dokunma hareketi tanıyıcının dokunmayı görünüm hiyerarşinize ilettiğinden emin olun: yukarıya singleTapGesture.cancelsTouchesInView = NO;eklendiviewDidLoad
Nick Ager

1

Belirli bir görünümün denetimlerine eklenen tüm hareket tanıyıcıları engellemek için tasarlanmış bir UIGestureRecognizer alt sınıfı oluşturdum.

WEPopover projemin bir parçası. Burada bulabilirsiniz .


1

parentView'in tüm tanıyıcıları için bir temsilci uygulayın ve tanıyıcıların eşzamanlı tetiklenmesinden sorumlu olan temsilciye hareketRecognizer yöntemini koyun:

func gestureRecognizer(UIGestureRecognizer,       shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool {
if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) {
    return true
    } else {
    return false
}

}

Çocukları tetiklemek istiyor ancak ebeveyn tanıyıcıları tetiklememek istiyorsanız başarısız yöntemleri kullanabilirsiniz:

https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate


0

Ben de popover yapıyordum ve bu şekilde yaptım

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

    if let _ = tableView.indexPathForRowAtPoint(tapLocation) {
        sender.cancelsTouchesInView = false
    }
    else {
        delegate?.menuDimissed()
    }
}

0

Kapatıp açabilirsiniz .... kodumda klavye görünmediğinde kapatmam gerektiğinden böyle bir şey yaptım, durumunuza uygulayabilirsiniz:

bu, viewdidload vb.

NSNotificationCenter    *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(notifyShowKeyboard:) name:UIKeyboardDidShowNotification object:nil];
[center addObserver:self selector:@selector(notifyHideKeyboard:) name:UIKeyboardWillHideNotification object:nil];

ardından iki yöntemi oluşturun:

-(void) notifyShowKeyboard:(NSNotification *)inNotification 
{
    tap.enabled=true;  // turn the gesture on
}

-(void) notifyHideKeyboard:(NSNotification *)inNotification 
{
    tap.enabled=false;  //turn the gesture off so it wont consume the touch event
}

Bunun yaptığı şey musluğu devre dışı bırakmaktır. Tap'ı bir örnek değişkenine dönüştürmek ve dealloc'ta serbest bırakmak zorunda kaldım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.