UITapGestureRecognizer olan bir görünüm içindeki UIB düğmesi


184

İle görüşüm var UITapGestureRecognizer. Görünüme dokunduğumda, bu görünümün üstünde başka bir görünüm beliriyor. Bu yeni görünümde üç düğme vardır. Şimdi bu düğmelerden birine bastığımda, düğmeler eylemini alamıyorum, yalnızca dokunma hareketi eylemini alıyorum. Artık bu düğmeleri kullanamıyorum. Olayları bu düğmelere almak için ne yapabilirim? Garip olan şey, düğmelerin hala vurgulanmış olmasıdır.

Dokunduktan sonra UITapGestureRecognizer'ı kaldıramıyorum. Çünkü onunla yeni görünüm de kaldırılabilir. Tam ekran video denetimleri gibi bir davranış istiyorum .

Yanıtlar:


256

Oyun kumandanızı veya görünümünüzü (hangisi hareket tanıyıcıyı oluşturursa) delegesi olarak ayarlayabilirsiniz UITapGestureRecognizer. Sonra delege uygulayabilirsiniz -gestureRecognizer:shouldReceiveTouch:. Uygulamanızda, dokunmanın yeni alt görünümünüze ait olup olmadığını test edebilir ve eğer öyleyse, jest tanıyıcıya bunu yok saymasını bildirebilirsiniz. Aşağıdaki gibi bir şey:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    // test if our control subview is on-screen
    if (self.controlSubview.superview != nil) {
        if ([touch.view isDescendantOfView:self.controlSubview]) {
            // we touched our control surface
            return NO; // ignore the touch
        }
    }
    return YES; // handle the touch
}

Başlık dosyamda, görünümüm UIGestoreRegognizerDelegate'i uyguladım ve .mi dosyamda yukarıdaki kodunuzu ekledim. Musluk asla bu yönteme girmez, doğrudan işleyicime gider. Herhangi bir fikir?
kmehta

1
@kmehta, büyük olasılıkla UIGestureRecognizer delege özelliğini ayarlamayı unuttunuz.
Till

Bu cevap, bir IBAction'ın dahil olduğu tüm vakaları ele almak için genelleştirilebilir mi?
Martin Wickman

@Martin IBAction, voidbunu derlemek için çalışma zamanında tespit için kullanmak üzere hiçbir bilgi bırakmaz. Ancak yapabileceğiniz şey, görünüm hiyerarşisinde yukarı çıkmak, test etmek UIControlve NOherhangi bir kontrol bulursanız geri dönmek .
Lily Ballard

Bunun için teşekkürler, bana yardımcı olur. düğmeniz UIImageView içindeyse, imageView öğesinin userInteractionEnabled öğesinin YES olarak ayarlandığından emin olun.
james075

156

Casey'nin Kevin Ballard'ın cevabının takibi:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
        if ([touch.view isKindOfClass:[UIControl class]]) {
            // we touched a button, slider, or other UIControl
            return NO; // ignore the touch
        }
    return YES; // handle the touch
}

Bu, temel olarak düğmeler, kaydırıcılar vb.Gibi tüm kullanıcı giriş kontrol türlerini çalıştırır


1
Bu setCancelsTouchesInView = NO, kontrollerle etkileşimde dokunma hareketini tetiklememeye eşlik edebilecek esnek bir çözümdür . Ancak daha iyi yazabilirsiniz:return ![touch.view isKindOfClass:[UIControl class]];
griga13

Swift 4+ çözümü: dönüş! (Touch.view UIControl olduğunu)
nteissler

103

Bu cevabı burada buldum: link

Ayrıca kullanabilirsiniz

tapRecognizer.cancelsTouchesInView = NO;

Bu, musluk tanıtıcısının tüm muslukları yakalayan tek kişi olmasını engeller

GÜNCELLEME - Michael bu özelliği açıklayan belgelere bağlantıdan bahsetti: cancelsTouchesInView


8
Bu kabul edilen cevap IMO olmalıdır. Bu, her alt görüntülemenin musluğu tek başına ele almasını sağlar ve ona bağlı bir tabrecognizer'a sahip görünümün musluğun 'yüksek atılmasını' önler. Tüm yapmak istediğiniz bir görünümü tıklanabilir hale getirmek için bir temsilci uygulamak gerek yok (ilk cevaplayıcı istifa / metin alanlarında klavyeyi gizlemek için vb ..) harika!
EeKay

4
Bu kesinlikle kabul edilen cevap olmalıdır. Bu cevap okumak götürdü Apple belgelerine açıkça jest tanıyıcı tanınan olayları girmesini subviews önleyecektir o yapar düzgün sürece bunu.
Michael van der Westhuizen

1
Bu işe yaradı ama şimdi benim için çalışmıyor ... belki de düğmenin altında bir scrollView var çünkü? Temsilciyi yukarıdaki cevaplardaki gibi uygulamak zorunda kaldım.
xissburg

7
Biraz denedikten sonra, bunun sadece hareket tanıyıcıyı içeren görünümün UIControl'ün bir kardeşi olması durumunda işe yarayacağını ve görünümün UIControl'ün üstü olması durumunda işe yaramayacağını fark ettim.
xissburg

6
Gerçekten amaçlandığı gibi çalışmıyor ... EVET, musluk tanıyıcı UIControl olayı yemek önlemek. Ancak yine de aynı anda 2 eylem olan tanıyıcı eylemini çalıştırır.
Tek Yin

71

Kevin Ballard'ın cevabının bir takibi olarak, aynı problemi yaşadım ve bu kodu kullandım:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([touch.view isKindOfClass:[UIButton class]]){
        return NO;
    }
    return YES;
}

Aynı etkiye sahiptir, ancak bu herhangi bir görünüm derinliğinde herhangi bir UIButton üzerinde çalışacaktır (UIButton'um birkaç görünüm derinliğindeydi ve UIGestureRecognizer'in delegesinin bir referansı yoktu.)


6
Burada bir sik olmak istemiyorum, ama gerçekten EVET ve HAYIR. Lütfen Kakao kurallarını kullanın. TRUE / FALSE / NULL, CoreFoundation-Layer içindir.
steipete

okuduğum kadarıyla, EVET ve NO aslında okunabilirlik için yaratıldı. okunabilirlik özneldir. herkesin aşırı endişe duymadığı yöntem ve özellik adlandırma kurallarından kaynaklanmaktadır. adlandırma kurallarına sıkı sıkıya bağlı kalmak istiyorsanız, evet, herhangi bir NSWhatever için EVET ve HAYIR'ı kullanın. Ancak, geliştiricilere anket yaparsanız bunlardan hangisinin daha okunabilir olduğu konusunda karışık görüşler elde edeceğinizi söyleyeceğim [düğme kümesiHidden: YES]; -VEYA- [düğme kümesiGizli: DOĞRU];
Pezevenk Suyu McJones

1
Söylemeye çalıştığım şey, isimlendirme kurallarının öncülünün okunabilir olması durumunda, bazı durumlarda öznel olduğunu inkar etmenin zor olduğunu düşünüyorum. eğer saf biriyseniz o ifadeyi anlamazsınız.
Pezevenk Suyu McJones

2
Bu öznel görünebilir ama kakao programlama bir süre sonra gerçekten daha semantik olarak doğru kod akışına girersiniz ... "DOĞRU" şimdi Objective-C yıllar sonra gerçekten yanlış görünüyor, çünkü çok "gizli" eşleşmiyor iyi.
Kendall Helmstetter Gelner

Ne zamandan beri bir UIButton'a UITouch Etkinliği Olarak Rötuş hareketine (ne zaman bir UIButton'a dokunarak oluşturulan olaydır) bir dokunma hareketini geçirebiliyor / geçirebiliyorsunuz? Bir dokunma hareketiyle zaten ilişkilendirilmiş 15 dokunma etkinliğine sahip bir düğme için dokunma hareketi tanıyıcı oluşturmak yinelenen ve yanlış görünüyor . Kodu görmek istiyorum, tahmin değil.
James Bush

10

İOS 6.0 ve sonraki sürümlerde, varsayılan kontrol eylemleri çakışan hareket algılayıcı davranışını önler. Örneğin, bir düğme için varsayılan eylem tek bir dokunuştur. Bir düğmenin üst görünümüne eklenmiş tek bir dokunma hareketi tanıyıcı varsa ve kullanıcı düğmeye dokunursa, düğmenin eylem yöntemi hareket tanıyıcı yerine dokunma olayını alır. Bu yalnızca aşağıdakileri içeren bir denetimin varsayılan eylemiyle çakışan hareket tanıma için geçerlidir: .....

Apple'ın API belgesinden


8

Bu cevaplar eksikti. Bu boolean işleminin nasıl kullanılacağı ile ilgili birden fazla mesajı okumak zorunda kaldım.

* .H dosyanıza bunu ekleyin

@interface v1ViewController : UIViewController <UIGestureRecognizerDelegate>

* .M dosyanıza bunu ekleyin

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

    NSLog(@"went here ...");

    if ([touch.view isKindOfClass:[UIControl class]])
    {
        // we touched a button, slider, or other UIControl
        return NO; // ignore the touch
    }
    return YES; // handle the touch
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.



    //tap gestrure
    UITapGestureRecognizer *tapGestRecog = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(screenTappedOnce)];
    [tapGestRecog setNumberOfTapsRequired:1];
    [self.view addGestureRecognizer:tapGestRecog];


// This line is very important. if You don't add it then your boolean operation will never get called
tapGestRecog.delegate = self;

}


-(IBAction) screenTappedOnce
{
    NSLog(@"screenTappedOnce ...");

}

7

Buradan yapmanın başka bir yolunu buldum . Her düğmenin içinde olup olmadığını dokunmayı algılar.

(1) pointInside: withEvent: (2) konum

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
       shouldReceiveTouch:(UITouch *)touch {
    // Don't recognize taps in the buttons
    return (![self.button1 pointInside:[touch locationInView:self.button1] withEvent:nil] &&
            ![self.button2 pointInside:[touch locationInView:self.button2] withEvent:nil] &&
            ![self.button3 pointInside:[touch locationInView:self.button3] withEvent:nil]);
}

Bunun için diğer tüm çözümleri denedim, ancak -pointInside: withEvent: yaklaşımı benim için çalışan tek şeydi.
Kenny Wyland

3

İşte Lily Ballard'ın benim için çalışan cevabının Swift versiyonu :

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if (scrollView.superview != nil) {
        if ((touch.view?.isDescendantOfView(scrollView)) != nil) { return false }
    }
    return true
}

3

Hızlı 5

Goblenli gözetim düğmesi

 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let _ = touch.view as? UIButton { return false }
    return true
}

Benim durumumda hitTest uygulamak benim için çalıştı. Düğmeli koleksiyon görünümüm vardı

Bu yöntem, hangi alt görünümün point(inside:with:)bir dokunma olayı alması gerektiğini belirlemek için her alt görünümün yöntemini çağırarak görünüm hiyerarşisini dolaşır. Eğer point(inside:with:)döner doğru belirtilen noktayı içeren en öndeki görünümü bulunana kadar, daha sonra Subview hiyerarşisi benzer kat edilmiştir.

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    guard isUserInteractionEnabled else { return nil }

    guard !isHidden else { return nil }

    guard alpha >= 0.01 else { return nil }

    guard self.point(inside: point, with: event) else { return nil }

    for eachImageCell in collectionView.visibleCells {
        for eachImageButton in eachImageCell.subviews {
            if let crossButton = eachImageButton as? UIButton {
                if crossButton.point(inside: convert(point, to: crossButton), with: event) {
                    return crossButton
                }
            }
        }
    }
    return super.hitTest(point, with: event)
}

1

Aşağıdaki boolean'ı ayarlayarak UITapGestureRecognizer'ın diğer etkinlikleri (düğmenize dokunma gibi) iptal etmesini durdurabilirsiniz:

    [tapRecognizer setCancelsTouchesInView:NO];

1

Senaryonuz şöyle ise:

Basit bir görünümünüz var ve bazı UIButton'larınız, UITextField denetimleri bu görünüme alt görünüm olarak eklenmiş. Artık kontroller (eklediğiniz alt görünümler) dışında görünümde başka bir yere dokunduğunuzda klavyeyi kapatmak istiyorsunuz

O zaman Çözüm:

Aşağıdaki yöntemi XYZViewController.m (görünümünüzü içeren) ekleyin

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.view endEditing:YES];
}

İlk önce @cdasher 'ın yanıtı ile denedim ama benim durumumda bile, düğmeye tıklayarak, ben ViewController benim "görünüm" olarak benim touch.view alıyorum ama benim "UIButton" kontrol. Birisi, dokunuşun view özelliğinin neden musluğun olduğu orijinal görünümü döndürmediğini söyleyebilir mi
NaveenRaghuveer

1
Kaydırma görünümünüz veya dokunma olaylarını talep eden başka bir görünümünüz varsa çalışmaz.
lkraider

1

Cdasher'ın cevabını optimize ederek,

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch 
{
    return ![touch.view isKindOfClass:[UIControl class]];
}
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.