UITapGestureRecognizer, UITableView didSelectRowAtIndexPath öğesini kırıyor


207

Klavye göründüğünde metin alanlarını yukarı kaydırmak için kendi işlevimi yazdım. Metin alanından uzağa dokunarak klavyeyi kapatmak için, dokunduğunuzda metin alanında UITapGestureRecognizerilk yanıtlayıcıyı istifaya özen gösteren bir klavye oluşturdum .

Şimdi UITableViewmetin alanının hemen altında metin alanı oluşturan ve kullanıcı metin girerken öğelerle dolduran bir otomatik tamamlama da oluşturdum .

Ancak, otomatik tamamlanan tablodaki girişlerden birini seçerken didSelectRowAtIndexPathçağrılmaz. Bunun yerine, dokunma hareketi tanıyıcı çağrılıyor ve sadece ilk yanıtlayıcıyı istifa ediyor gibi görünüyor.

Dokunma hareketi tanıyıcıya dokunma mesajını aşağıya geçmeye devam etmesini söylemenin bir yolu olduğunu tahmin ediyorum UITableView, ama bunun ne olduğunu anlayamıyorum . Herhangi bir yardım çok takdir edilecektir.


Yanıtlar:


258

Tamam, nihayet bazı jest tanıyıcı dokümanlar arasında arama yaptıktan sonra buldum.

Çözüm, aşağıdakileri uygulamak UIGestureRecognizerDelegateve eklemekti:

////////////////////////////////////////////////////////////
// UIGestureRecognizerDelegate methods

#pragma mark UIGestureRecognizerDelegate methods

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:autocompleteTableView]) {

        // Don't let selections of auto-complete entries fire the 
        // gesture recognizer
        return NO;
    }

    return YES;
}

Bunu hallettim. Umarım bu başkalarına da yardımcı olacaktır.


Ayrıca muhtemelen metin alanına dokunmanız gerekir.
Steven Kramer

5
Daha fazla şansım vardıreturn ![touch.view isKindOfClass:[UITableViewCell class]];
Josh Paradroid

1
@Josh Hücrenin içindeki etiketlere dokunursanız çalışmaz. Aslında Jason'ın cevabı ihtiyacım olan şey için mükemmel çalışıyor.
William T.

Ama bu sorunun cevabı değil, değil mi? Bu bir / veya. Temelde tablo görünümünde herhangi bir dokunuş jest tanıyıcı ile önemsiz .... istediğiniz şey jest tanıyıcı ve tablo görünümü seçim çalışması için
PostCodeism

5
@Durgaprasad Hücreleri seçerken hareket tanıtıcısını açıkça devre dışı bırakmak için, kendisini hariç tutmak için ![touch.view isEqual: self.tableView] &&önce a ekledim (çünkü argüman-görünümü alıcı-görünümü ise geri döner ). Umarım aynı sonucu isteyen diğer insanlara yardımcı olur. [touch.view isDescendantOfView: self.tableView]tableViewisDescendantOfView:YES
anneblue

220

Bu sorunu çözmenin en kolay yolu:

UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] 
    initWithTarget:self action:@selector(tap:)];
[tapRec setCancelsTouchesInView:NO];

Bu UIGestureRecognizer, musluğun tanınmasını ve ayrıca dokunmanın bir sonraki yanıtlayıcıya geçmesini sağlar. Bu yöntemin istenmeyen bir sonucu,UITableViewCell başka bir görünüm denetleyicisini iten bir ekranınız varsa. Kullanıcı klavyeyi kapatmak için satıra dokunursa, hem klavye hem de itme tanınır. Şunu düşündüğünüzden şüpheliyim, ancak bu yöntem birçok durum için yeterli.

Ayrıca, Robert'in cevabını genişleterek, söz konusu tablo görünümüne bir işaretçiniz varsa, bir dizeye dönüştürmek yerine Apple'ın sınıfını doğrudan karşılaştırabilir ve Apple'ın isimlendirmeyi değiştirmediğini umabilirsiniz:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
     shouldReceiveTouch:(UITouch *)touch
{
    if([touch.view class] == tableview.class){
        return //YES/NO
    }

    return //YES/NO

}

Unutmayın, ayrıca UIGestureRecognizeriçinde bu kodla bir temsilci olduğunu beyan etmelisiniz .


4
Teşekkürler, setCancelsTouchesInView her şeyi değiştirir :)
PostCodeism

Bir alt görünüme dokunuyor olabileceğiniz için isDescendantOfView, classbunun eşit olup olmadığını kontrol etmek yerine yine de kullanmak isteyebilirsiniz. İhtiyacınız olan şeye bağlıdır.
shim

71

Ayarlamak cancelsTouchesInViewTanıyıcıyı false olarak . Aksi takdirde, dokunuşu kendisi için "tüketir" ve tablo görünümüne iletmez. Bu yüzden seçim etkinliği asla gerçekleşmez.

örneğin swift

let tapOnScreen: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "CheckTheTime")
tapOnScreen.cancelsTouchesInView = false
view.addGestureRecognizer(tapOnScreen)

1
Bunun için çok teşekkürler!
Bishan

1
Bu Swift'in en temizidir. Teşekkürler
Dx_

tablo görünümünde hücreler içinde iyi çalışır, güzel ve basit!
Abdoelrhman

@laoyur very true
bLacK hoLE

dokunun jest yerine hala hücre tıklaması var
famfamfam

42

Ve Swift için (@Jason'un cevabına dayanarak):

class MyAwesomeClass: UIViewController, UIGestureRecognizerDelegate

private var tap: UITapGestureRecognizer!

override func viewDidLoad() {
   super.viewDidLoad()

   self.tap = UITapGestureRecognizer(target: self, action: "viewTapped:")
   self.tap.delegate = self
   self.view.addGestureRecognizer(self.tap)
}

// UIGestureRecognizerDelegate method
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view?.isDescendantOfView(self.tableView) == true {
        return false
    }
    return true
}

Çok teşekkürler bu zamanımı kurtardı.
Bishan

22

Bir tablo görünümü üzerinde bir musluk hareketi eklemek ama aynı zamanda hücre seçimine izin vermek için daha iyi bir çözüm olabilir:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if gestureRecognizer is UITapGestureRecognizer {
        let location = touch.locationInView(tableView)
        return (tableView.indexPathForRowAtPoint(location) == nil)
    }
    return true
}

Ben sadece kullanıcının dokunduğu ekranın noktasında bir hücre arıyorum. Hiçbir dizin yolu bulunmazsa, hareketin dokunuşu almasına izin veririm, aksi takdirde iptal ederim. Benim için harika çalışıyor.


1
Bu çözüm sallanıyor! Hücreyi masanın boş alanından ayırmak için çok yararlı ..
Alessandro Ornano

18

Sadece basitçe belirlenen kodların yazma bloklarına gerek yoktur düşünüyorum cancelsTouchesInViewiçin falsevarsayılan olarak bu, sizin jest nesnesi için trueve sadece bunu ayarlamak zorunda false. UITapGestureKodunuzda nesne kullanıyorsanız ve UIScrollView(tableview, collectionview) kullanıyorsanız , bu özelliği ayarlayınfalse

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

Bu yanıtı gönderdiğiniz için teşekkür ederiz
Gustavo Baiocchi Costa

14

Benzer bir çözüm, gestureRecognizer:shouldReceiveTouch: hangi eylemin gerçekleştirileceğini belirlemek için görünüm sınıfını kullanarak . Bu yaklaşımın, tabloyu doğrudan çevreleyen bölgedeki muslukları maskelememe avantajı vardır (bu alanın görünümleri UITableView örneklerinden hala iner, ancak hücreleri temsil etmez).

Bu aynı zamanda tek bir görünümde (ekstra kod eklemeden) birden fazla tabloyla çalıştığı gibi bir bonus da sağlar.

Dikkat: Apple'ın sınıf adını değiştirmeyeceği varsayımı var.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return ![NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"];
}

1
Sınıf adı dizesini ortadan kaldırmak için bu satırı kullanınreturn ![[touch.view.superview class] isSubclassOfClass:[UITableViewCell class]];
Nianliang

7

İçin Swift 4.2 çözümü uygulamak oldu UIGestureRecognizerDelegateve aşağıdakileri ekleyin:

extension ViewController : UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view!.isDescendant(of: tblView) {
            return false
        }
        return true
    }
}

tablo görünümüne tıkladığınızda, bu temsilci yöntemi false ve didSelectRowAtIndexPathtablo görünümü yönteminin düzgün çalışmasını döndürür .


1
Bu cevabı gönderdiğin için teşekkürler dostum, zamanımı kurtarıyorsun.
Jay Bhalani

4

Dokunmatik hareket işlevinin yalnızca kullanıcı tablo görünümünün dışına dokunduğunda çağrılmasını istediğim farklı bir durum vardı . Kullanıcı tablo görünümünün içine dokunduysa, dokunma hareketi işlevi çağrılmamalıdır. Ayrıca, dokunma hareketi işlevi çağrılırsa, dokunma olayını tüketmek yerine dokunulan görünüme geçirmelidir.

Ortaya çıkan kod, Abdulrahman Masoud'un yanıtı ve Nikolaj Nielsen'in yanıtıdır.

extension MyViewController: UIGestureRecognizerDelegate {
    func addGestureRecognizer() {
        let tapOnScreen = UITapGestureRecognizer(target: self,
                                                action: #selector(functionToCallWhenUserTapsOutsideOfTableView))

        // stop the gesture recognizer from "consuming" the touch event, 
        // so that the touch event can reach other buttons on view.
        tapOnScreen.cancelsTouchesInView = false

        tapOnScreen.delegate = self

        self.view.addGestureRecognizer(tapOnScreen)
    }

    // if your tap event is on the menu, don't run the touch event.
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view?.isDescendant(of: self.tableView) == true {
            return false
        }
        return true
    }

    @objc func functionToCallWhenUserTapsOutsideOfTableView() {

        print("user tapped outside table view")
    }
}

Ve içinde MyViewController sınıfının sahip sınıf UITableView, içinde onViewDidLoad(), aramaya emin oldum addGestureRecognizer():

class MyViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        ...
        self.addGestureRecognizer()
        ...
    }
    ...
}

3

Sadece set cancels touches in viewiçinfalse altından herhangi jest için (table/collection)View:

- Arayüzler arası

InterfaceBuilder'da

- Kod

<#gestureRecognizer#>.cancelsTouchesInView = false

devre dışı bırakmak istersem bu sadece didSelectRowAt onunla çalışmasına izin vermek değil?
Eyad Shokry

1

Geç olsa da ve birçok kişi yukarıdaki önerilerin işe yaradığını fark etse de, Jason veya TMilligan'ın yöntemlerini çalıştıramadım.

Statik tableView yalnızca Sayı Klavye kullanarak Sayı girişleri almak textFields içeren birden çok hücre ile var . Bu benim için ideal:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if(![touch.view isKindOfClass:[UITableViewCell class]]){

        [self.firstTF resignFirstResponder];
        [self.secondTF resignFirstResponder];
        [self.thirdTF resignFirstResponder];
        [self.fourthTF resignFirstResponder];

        NSLog(@"Touches Work ");

        return NO;
    }
    return YES;
}

Bunu uyguladığınızdan emin olun <UIGestureRecognizerDelegate> .h dosyanıza .

Bu satır, ![touch.view isKindOfClass:[UITableViewCell class]]bir tableViewCell'in dokunulduğunu kontrol eder ve etkin klavyeleri kapatır.


1

Basit çözüm, dokunuşları elde etmek için UITableViewCell'de UIControl örneklerini kullanmaktır. Dokunma almak için userInteractionEnables == NO ile UIControl'e herhangi bir görünüm ekleyebilirsiniz.


0

İşte, klavyenin gösterilip gösterilmeyeceğine doğrudan tanıyıcının mustReceiveTouch öğesini bağlayan çözümüm.

Dokunma hareketi tanıyıcı temsilcinizde:

#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([PFXKeyboardStateListener sharedInstance].visible) {
        return YES;
    }

    return NO;
}

Ve PFXKeyboardStateListener.h:

@interface PFXKeyboardStateListener : NSObject
{
    BOOL _isVisible;
}

+ (PFXKeyboardStateListener *)sharedInstance;

@property (nonatomic, readonly, getter=isVisible) BOOL visible;

@end

Ve PFXKeyboardStateListener.m:

static PFXKeyboardStateListener *sharedInstance;

@implementation PFXKeyboardStateListener

+ (PFXKeyboardStateListener *)sharedInstance
{
    return sharedInstance;
}

+ (void)load
{
    @autoreleasepool {
        sharedInstance = [[self alloc] init];
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (BOOL)isVisible
{
    return _isVisible;
}

- (void)didShow
{
    _isVisible = YES;
}

- (void)didHide
{
    _isVisible = NO;
}

- (id)init
{
    if ((self = [super init])) {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(didShow) name:UIKeyboardDidShowNotification object:nil];
        [center addObserver:self selector:@selector(didHide) name:UIKeyboardWillHideNotification object:nil];
    }
    return self;
}

@end

Klavye dinleyicisinin singleton desenini güncellemek isteyebilirsiniz, henüz anlamadım. Umarım bu benim için olduğu kadar herkes için de işe yarar. ^^


0

UIGestureRecognizer delegesi için bu yöntemi uygulayın:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch
{
  UIView *superview = touch.view;
  do {
    superview = superview.superview;
    if ([superview isKindOfClass:[UITableViewCell class]])
      return NO;
  } while (superview && ![superview isKindOfClass:[UITableView class]]);

  return superview != nil;
}

0

Hızlı bir şekilde bunu içeride kullanabilirsiniz

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if CheckTheTime() == true {
        // do something 
    }else{
    }
}

func CheckTheTime() -> Bool{
    return true
}

0

Benim durumum uisearchbar ve self.view üzerinde uitableview dahil farklıydı. Görünüme dokunarak uisearchbar klavyesini kapatmak istedim.

var tapGestureRecognizer:UITapGestureRecognizer?

override func viewWillAppear(animated: Bool) {
    tapGestureRecognizer = UITapGestureRecognizer(target: self, action:Selector("handleTap:"))
}

UISearchBar Delege Yöntemlerinde:

func searchBarShouldBeginEditing(searchBar: UISearchBar) -> Bool {
    view.addGestureRecognizer(tapGestureRecognizer!)
    return true
}
func searchBarShouldEndEditing(searchBar: UISearchBar) -> Bool {
    view.removeGestureRecognizer(tapGestureRecognizer!)
    return true
}

Kullanıcı self.view öğesine dokunduğunda:

func handleTap(recognizer: UITapGestureRecognizer) {
    sampleSearchBar.endEditing(true)
    sampleSearchBar.resignFirstResponder()
}

0

Swift 5, Mayıs 2020.

Bir textField ve metin girerken görünür hale gelen bir tableView var.

Başlangıç ​​hali Bu yüzden tableViewCell veya başka bir şeye dokunduğumda 2 farklı olay istiyorum.

Klavye ve tablo görünümü gösteriliyor

Önce tapGestureRecognizer ekliyoruz.

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

@objc func viewTapped() {
        view.endEditing(true)
}

Sonra UIGestureRecognizerDelegate'e aşağıdaki denetimi ekliyoruz:

extension StadtViewController: UIGestureRecognizerDelegate {

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if touch.view?.isDescendant(of: self.tableView) == true {
        return false
    } else {
        view.endEditing(true)
        return true
    }
}

}

Önce klavyeyi gizlemek istersem, tableView görünür ve dokunuşlarıma yanıt verir.

resim açıklamasını buraya girin


-1

Bu benim yukarıdaki cevaplara dayanan çözümüm ... Benim için çalıştı ...

//Create tap gesture for menu transparent view
UITapGestureRecognizer *rightTableTransparentViewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(rightTableTransparentViewTapMethod:)];
[rightTableTransparentViewTap setCancelsTouchesInView:NO];
[_rightTableTransparentView addGestureRecognizer:rightTableTransparentViewTap];

- (void)rightTableTransparentViewTapMethod:(UITapGestureRecognizer *)recognizer {
    //Write your code here
}

-1

SORUN: Benim durumumda, asıl olarak her collectionView hücresine bir düğme yerleştirdim ve hücreyi dolduracak kısıtlamaları ayarladım, böylece hücre tıklandığında düğmeyi tıklayacaktı, ancak düğmeler işlevi boştu, böylece hiçbir şey yoktu görünmektedir.

Düzeltme: Bu işlemi, koleksiyon görünümü hücresinden düğmeyi kaldırarak çözdü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.