UITapGestureRecognizer phá vỡ UITableView didSelectRowAtIndexPath


207

Tôi đã viết chức năng của riêng mình để cuộn các trường văn bản lên khi bàn phím xuất hiện. Để loại bỏ bàn phím bằng cách nhấn ra khỏi trường văn bản, tôi đã tạo một thao tác UITapGestureRecognizerchăm sóc từ chức phản hồi đầu tiên trên trường văn bản khi nhấn đi.

Bây giờ tôi cũng đã tạo một tự động hoàn thành cho UITableViewtrường văn bản tạo ra ngay bên dưới trường văn bản và điền vào đó với các mục khi người dùng nhập văn bản.

Tuy nhiên, khi chọn một trong các mục trong bảng tự động hoàn thành, didSelectRowAtIndexPathsẽ không được gọi. Thay vào đó, có vẻ như bộ nhận dạng cử chỉ nhấn đang được gọi và chỉ từ chức phản hồi đầu tiên.

Tôi đoán có một số cách để nói với trình nhận dạng cử chỉ nhấn để tiếp tục chuyển thông điệp nhấn xuống UITableView, nhưng tôi không thể hiểu nó là gì. Bất kỳ trợ giúp sẽ được rất đánh giá cao.


Câu trả lời:


258

Ok, cuối cùng đã tìm thấy nó sau khi một số tìm kiếm thông qua các tài liệu nhận dạng cử chỉ.

Giải pháp là thực hiện UIGestureRecognizerDelegatevà thêm vào như sau:

////////////////////////////////////////////////////////////
// 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;
}

Điều đó đã chăm sóc nó. Hy vọng điều này sẽ giúp những người khác là tốt.


Bạn cũng cần bỏ qua vòi trên trường văn bản.
Steven Kramer

5
Tôi đã có nhiều may mắn hơn vớireturn ![touch.view isKindOfClass:[UITableViewCell class]];
Josh Paradroid

1
@Josh Điều này không hoạt động nếu bạn nhấn vào nhãn bên trong ô. Thật ra câu trả lời của Jason hoạt động hoàn hảo cho những gì tôi cần.
William T.

Nhưng đây không phải là câu trả lời cho vấn đề của bạn, phải không? Đây là một hoặc / hoặc. Về cơ bản, bất kỳ chạm nào trên chế độ xem bảng đều không đáng kể với trình nhận dạng cử chỉ .... điều bạn muốn là CẢ HAI trình nhận dạng cử chỉ VÀ lựa chọn chế độ xem bảng để hoạt động
PostCodeism

5
@Durgaprasad Để vô hiệu hóa nhận dạng cử chỉ rõ ràng khi chọn các ô tôi đã thêm vào ![touch.view isEqual: self.tableView] &&trước [touch.view isDescendantOfView: self.tableView]để loại trừ tableViewchính nó (vì isDescendantOfView:cũng trả về YESnếu chế độ xem đối số là chính chế độ xem của người nhận). Hy vọng rằng sẽ giúp những người khác muốn có kết quả tương tự.
anneblue

220

Cách dễ nhất để giải quyết vấn đề này là:

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

Điều này cho phép UIGestureRecognizernhận biết vòi và cũng chuyển cảm ứng cho người phản hồi tiếp theo. Một hậu quả không lường trước của phương pháp này là nếu bạn có một UITableViewCellmàn hình đẩy bộ điều khiển xem khác. Nếu người dùng chạm vào hàng để tắt bàn phím, cả bàn phím và phím ấn sẽ được nhận ra. Tôi nghi ngờ đây là những gì bạn dự định, nhưng phương pháp này là đủ cho nhiều tình huống.

Ngoài ra, mở rộng câu trả lời của Robert, nếu bạn có một con trỏ tới bảng xem xét, thì bạn có thể so sánh trực tiếp lớp của nó thay vì phải chuyển đổi thành một chuỗi và hy vọng Apple không thay đổi danh pháp:

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

    return //YES/NO

}

Hãy nhớ rằng, bạn cũng phải khai báo UIGestureRecognizerđể có một đại biểu có mã này trong đó.


4
Cảm ơn, setCancelsTouchesInView thay đổi mọi thứ :)
PostCodeism

Có thể vẫn muốn sử dụng isDescendantOfViewthay vì chỉ kiểm tra xem classcó bằng nhau không, vì bạn có thể đang nhấn vào một khung nhìn phụ. Phụ thuộc vào những gì bạn cần.
shim

71

Đặt cancelsTouchesInViewbộ nhận dạng của bạn thành false. Mặt khác, nó "tiêu thụ" cảm ứng cho chính nó và không chuyển nó vào chế độ xem bảng. Đó là lý do tại sao sự kiện tuyển chọn không bao giờ xảy ra.

ví dụ như trong swift

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

1
Thanx rất nhiều cho điều này!
Bishan

1
Đây là sạch nhất trong Swift. Cảm ơn
Dx_ 8/8/2016

hoạt động tốt trong các tế bào trong viewview, đáng yêu và đơn giản!
Abdoelrhman

@laoyur rất đúng
bLacK hoLE

vẫn nhận được nhấp chuột di động thay vì cử chỉ nhấn
famfamfam

42

Và đối với Swift (dựa trên câu trả lời từ @Jason):

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
}

Thanx rất nhiều điều này tiết kiệm thời gian của tôi.
Bishan

22

Tôi có thể có một giải pháp tốt hơn để thêm cử chỉ nhấn vào chế độ xem bảng nhưng cho phép chọn ô cùng lúc:

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

Tôi chỉ tìm một ô ở điểm trên màn hình nơi người dùng đang chạm. Nếu không tìm thấy đường dẫn chỉ mục thì tôi để cử chỉ nhận được cảm ứng nếu không tôi sẽ hủy nó. Đối với tôi nó hoạt động rất tốt.


1
Giải pháp này đá! Rất hữu ích để phân biệt ô với trường trống của bảng ..
Alessandro Ornano

18

Tôi nghĩ rằng không cần phải viết các khối mã chỉ đơn giản được đặt cancelsTouchesInViewthành falsecho đối tượng cử chỉ của bạn, theo mặc định nó truevà bạn chỉ cần đặt nó false. Nếu bạn đang sử dụng UITapGestuređối tượng trong mã của mình và cũng đang sử dụng UIScrollView(viewview, Collectionview) thì hãy đặt thuộc tính nàyfalse

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

Cảm ơn bạn đã đăng câu trả lời này
Gustavo Baiocchi Costa

14

Một giải pháp tương tự là triển khai gestureRecognizer:shouldReceiveTouch:bằng cách sử dụng lớp của khung nhìn để xác định hành động nào cần thực hiện. Cách tiếp cận này có ưu điểm là không che các vòi trong khu vực xung quanh bảng (các chế độ xem của các khu vực này vẫn giảm xuống từ các phiên bản UITableView, nhưng chúng không đại diện cho các ô).

Điều này cũng có một phần thưởng mà nó hoạt động với nhiều bảng trên một chế độ xem (không thêm mã bổ sung).

Hãy cẩn thận: có một giả định rằng Apple sẽ không thay đổi tên lớp.

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

1
Để loại bỏ chuỗi tên lớp, hãy sử dụng dòng nàyreturn ![[touch.view.superview class] isSubclassOfClass:[UITableViewCell class]];
Nianliang

7

Đối với Swift 4.2 , giải pháp là triển khai UIGestureRecognizerDelegatevà thêm vào như sau:

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

khi bạn bấm vào xem bảng, phương thức ủy nhiệm này trả về false và didSelectRowAtIndexPathphương thức xem bảng hoạt động đúng.


1
cảm ơn bạn đã đăng câu trả lời này, bạn tiết kiệm thời gian của tôi.
Jay Bhalani

4

Tôi đã có một tình huống khác nhau, nơi tôi muốn các chức năng cảm ứng cử chỉ được gọi là chỉ khi người dùng khai thác bên ngoài của xem bảng. Nếu người dùng gõ nhẹ vào chế độ xem bảng, thì không nên gọi chức năng cử chỉ chạm. Ngoài ra, nếu chức năng cử chỉ cảm ứng được gọi, nó vẫn sẽ chuyển sự kiện chạm vào chế độ xem được bật hơn là tiêu thụ nó.

Mã kết quả là sự kết hợp giữa câu trả lời của Abdulrahman Masoud và câu trả lời của Nikolaj Nielsen.

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")
    }
}

Và trong MyViewControllerlớp, lớp có UITableView, trong onViewDidLoad(), tôi chắc chắn gọi addGestureRecognizer():

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

3

Chỉ cần thiết lập cancels touches in viewđể falsecho bất kỳ cử chỉ bên dưới (table/collection)View:

- Liên bộ

Interfacebuilder

- Mã

<#gestureRecognizer#>.cancelsTouchesInView = false

Điều gì xảy ra nếu tôi muốn vô hiệu hóa nó hoạt động không chỉ cho phép didSelectRowAt hoạt động với nó?
Eyad Shokry

1

Mặc dù đã muộn và nhiều người thấy rằng các đề xuất trên hoạt động tốt, tôi không thể làm cho các phương pháp của Jason hoặc TMilligan hoạt động.

Tôi có một bảng Xem tĩnh với nhiều ô chứa textField nhận các đầu vào Số chỉ bằng Bàn phím số. Đây là lý tưởng cho tôi:

- (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;
}

Đảm bảo rằng bạn đã thực hiện điều này <UIGestureRecognizerDelegate>trong tệp .h của bạn.

Dòng này ![touch.view isKindOfClass:[UITableViewCell class]]kiểm tra xem một bảngViewCell đã được gõ và loại bỏ bất kỳ bàn phím hoạt động nào.


1

Giải pháp đơn giản là sử dụng các phiên bản UIControl trong UITableViewCell để liên lạc. Bạn có thể thêm bất kỳ chế độ xem nào với userInteractionEnables == NO vào UIControl để nhận vòi.


0

Đây là giải pháp của tôi, liên quan trực tiếp đến bộ nhận diện nên nhận biết trực tiếp về việc bàn phím có hiển thị hay không.

Trong đại biểu nhận dạng cử chỉ nhấn của bạn:

#pragma mark - UIGestureRecognizerDelegate

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

    return NO;
}

PFXKeyboardStateListener.h:

@interface PFXKeyboardStateListener : NSObject
{
    BOOL _isVisible;
}

+ (PFXKeyboardStateListener *)sharedInstance;

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

@end

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

Bạn có thể muốn cập nhật mẫu singleton của trình nghe bàn phím, tôi chưa nhận được nó. Hy vọng điều này làm việc cho những người khác cũng như nó làm việc cho tôi. ^^


0

Thực hiện phương pháp này cho đại biểu của UIGestureRecognizer:

- (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

Trong swift bạn có thể sử dụng bên trong này

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

func CheckTheTime() -> Bool{
    return true
}

0

Trường hợp của tôi là khác nhau bao gồm một uisearchbar và uitableview trên self.view. Tôi muốn loại bỏ bàn phím uisearchbar bằng cách chạm vào màn hình.

var tapGestureRecognizer:UITapGestureRecognizer?

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

Trên các phương thức đại biểu UISearchBar:

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

Khi người dùng chạm vào self.view:

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

0

Swift 5, tháng 5 năm 2020.

Tôi có textField và tableView sẽ hiển thị khi tôi nhập văn bản.

Trạng thái ban đầu Vì vậy, tôi muốn 2 sự kiện khác nhau khi tôi nhấn vào bảngViewCell hoặc một cái gì đó khác.

Bàn phím và bảng xem đang được hiển thị

Đầu tiên chúng ta thêm tapGestureRecognizer.

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

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

Sau đó, chúng tôi thêm kiểm tra sau vào UIGestureRecognizerDelegate:

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
    }
}

}

Nếu tôi muốn ẩn bàn phím trước, bảngView vẫn hiển thị và phản hồi với các vòi của tôi.

nhập mô tả hình ảnh ở đây


-1

Đây là giải pháp của tôi dựa trên các câu trả lời ở trên ... Nó hiệu quả với tôi ...

//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

VẤN ĐỀ: Trong trường hợp của tôi, vấn đề là ban đầu tôi đã đặt một nút trong mỗi ô của bộ sưu tập và đặt các ràng buộc để điền vào ô, để khi ô được nhấp, nó sẽ nhấp vào nút, tuy nhiên chức năng của nút trống nên không có gì dường như đang xảy ra

CỐ ĐỊNH: Tôi đã sửa lỗi này bằng cách loại bỏ nút khỏi ô xem bộ sưu tập.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.