iOS - meneruskan semua sentuhan melalui tampilan


145

Saya memiliki tampilan yang terhampar di atas banyak tampilan lainnya. Saya hanya menggunakan overaly untuk mendeteksi beberapa sentuhan pada layar, tetapi selain itu saya tidak ingin tampilan menghentikan perilaku tampilan lain di bawahnya, yaitu tampilan gulir, dll. Bagaimana cara meneruskan semua sentuhan melalui tampilan overlay ini? Ini adalah subkals dari UIView.


2
Sudahkah Anda memecahkan masalah Anda? Jika ya maka Mohon terima jawaban agar lebih mudah bagi orang lain untuk menemukan solusinya karena saya menghadapi masalah yang sama.
Khushbu Desai

jawaban ini berfungsi dengan baik stackoverflow.com/a/4010809/4833705
Lance Samaria

Jawaban:


124

Saya hanya perlu menonaktifkan interaksi pengguna!

Objective-C:

myWebView.userInteractionEnabled = NO;

Cepat:

myWebView.isUserInteractionEnabled = false

Ini berfungsi dalam situasi saya di mana saya memiliki subview dari tombol. Saya menonaktifkan interaksi di subview dan tombol berfungsi.
simple_code

2
Di Swift 4,myWebView.isUserInteractionEnabled = false
Louis 'LYRO' Dupont

120

Untuk meneruskan sentuhan dari tampilan hamparan ke tampilan di bawahnya, terapkan metode berikut di UIView:

Objective-C:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    NSLog(@"Passing all touches to the next view (if any), in the view stack.");
    return NO;
}

Cepat 5:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    print("Passing all touches to the next view (if any), in the view stack.")
    return false
}

4
Saya memiliki masalah yang sama, tetapi yang saya inginkan adalah jika saya memperbesar / memutar / memindahkan tampilan dengan isyarat maka itu harus mengembalikan ya dan untuk acara istirahat harus mengembalikan tidak, bagaimana saya bisa mencapai ini?
Minakshi

@Minakshi itu pertanyaan yang sama sekali berbeda, ajukan pertanyaan baru untuk itu.
Fattie

tetapi perlu diketahui bahwa pendekatan sederhana ini TIDAK MEMBERIKAN KAMU MEMILIKI tombol dan seterusnya DI ATAS lapisan yang dimaksud!
Fattie

@ Minakshi hai, apakah Anda menemukan jawaban untuk pertanyaan Anda?
Lance Samaria

57

Ini adalah utas lama, tetapi muncul pada pencarian, jadi saya pikir saya akan menambahkan 2c saya. Saya memiliki UIView yang mencakup subview, dan hanya ingin mencegat sentuhan yang mengenai salah satu subview, jadi saya memodifikasi jawaban PixelCloudSt untuk

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    for (UIView* subview in self.subviews ) {
        if ( [subview hitTest:[self convertPoint:point toView:subview] withEvent:event] != nil ) {
            return YES;
        }
    }
    return NO;
}

1
Anda perlu membuat subclass UIView kustom (atau subclass dari subclass UIView lainnya seperti UIImageView atau apa pun) dan mengganti fungsi ini. Intinya, subclass dapat memiliki '@interface' yang sepenuhnya kosong di file .h, dan fungsi di atas adalah seluruh konten '@implementation'.
fresidue

Terima kasih, inilah yang saya butuhkan untuk memberikan sentuhan melalui ruang kosong UIView saya, untuk ditangani oleh pemandangan di belakang. +100
Danny

45

Versi jawaban @fresidue yang ditingkatkan. Anda dapat menggunakan UIViewsubclass ini sebagai tampilan transparan yang meneruskan sentuhan di luar subviewnya. Implementasi di Objective-C :

@interface PassthroughView : UIView

@end

@implementation PassthroughView

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    for (UIView *view in self.subviews) {
        if (!view.hidden && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
            return YES;
        }
    }
    return NO;
}

@end

.

dan di Swift:

class PassthroughView: UIView {
  override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    return subviews.contains(where: {
      !$0.isHidden
      && $0.isUserInteractionEnabled
      && $0.point(inside: self.convert(point, to: $0), with: event)
    })
  }
}

TIP:

Misalnya Anda memiliki panel "dudukan" besar, mungkin dengan tampilan meja di belakang. Anda membuat panel "pemegang" PassthroughView. Sekarang akan bekerja, Anda dapat menggulir tabel "melalui" pemegang ".

Tapi!

  1. Di atas panel "pemegang" Anda memiliki beberapa label atau ikon. Jangan lupa, tentu saja itu semua harus ditandai interaksi pengguna diaktifkan NONAKTIF!

  2. Di atas panel "pemegang" Anda memiliki beberapa tombol. Jangan lupa, tentu saja itu hanya harus ditandai interaksi pengguna diaktifkan ON!

  3. Perhatikan bahwa agak membingungkan, "pemegang" itu sendiri - tampilan yang Anda gunakan PassthroughView- harus ditandai interaksi pengguna diaktifkan AKTIF ! Itu ON !! (Jika tidak, kode di PassthroughView tidak akan pernah dipanggil.)


1
Saya telah menggunakan solusi yang diberikan untuk Swift. Ini bekerja pada ios 11 tetapi mengalami crash setiap kali di iOS 10. Menampilkan Akses Buruk. Ada ide?
Soumen

Anda menyelamatkan hari-hari saya, itu berhasil untuk memberikan sentuhan ke UIViews di bawah ini.
AaoIi

1
Jangan lupa juga untuk memeriksa$0.isUserInteractionEnabled
Sean Thielen

8

Saya memiliki masalah serupa dengan UIStackView (tetapi bisa jadi tampilan lain). Konfigurasi saya adalah sebagai berikut: Lihat pengontrol dengan containerView dan StackView

Ini adalah kasus klasik di mana saya memiliki wadah yang perlu ditempatkan di latar belakang, dengan tombol di samping. Untuk tujuan tata letak, saya menyertakan tombol dalam UIStackView, tetapi sekarang bagian tengah (kosong) dari penyadapan stackView menyentuh :-(

Apa yang saya lakukan adalah membuat subclass UIStackView dengan properti yang mendefinisikan subView yang harus dapat disentuh. Sekarang, setiap sentuhan pada tombol samping (termasuk dalam larik * viewsWithActiveTouch *) akan diberikan ke tombol, sementara setiap sentuhan pada stackview di mana pun selain tampilan ini tidak akan dicegat, dan oleh karena itu diteruskan ke apa pun yang ada di bawah tumpukan melihat.

/** Subclass of UIStackView that does not accept touches, except for specific subviews given in the viewsWithActiveTouch array */
class NoTouchStackView: UIStackView {

  var viewsWithActiveTouch: [UIView]?

  override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {

    if let activeViews = viewsWithActiveTouch {
        for view in activeViews {
           if CGRectContainsPoint(view.frame, point) {
                return view

           }
        }
     }
     return nil
   }
}

6

Jika tampilan tempat Anda ingin meneruskan sentuhan tidak kebetulan merupakan subview / superview, Anda dapat menyiapkan properti kustom di subclass UIView seperti ini:

@interface SomeViewSubclass : UIView {

    id forwardableTouchee;

}
@property (retain) id forwardableTouchee;

Pastikan untuk mensintesiskannya di .m Anda:

@synthesize forwardableTouchee;

Dan kemudian sertakan yang berikut ini di salah satu metode UIResponder Anda seperti:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    [self.forwardableTouchee touchesBegan:touches withEvent:event];

}

Di mana pun Anda membuat instance UIView, setel properti forwardableTouchee ke tampilan apa pun yang Anda inginkan untuk meneruskan acara:

    SomeViewSubclass *view = [[[SomeViewSubclass alloc] initWithFrame:someRect] autorelease];
    view.forwardableTouchee = someOtherView;

5

Saya perlu memberikan sentuhan melalui UIStackView. UIView di dalamnya transparan, tetapi UIStackView menggunakan semua sentuhan. Ini berhasil untuk saya:

class PassThrouStackView: UIStackView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
        if view == self {
            return nil
        }
        return view
    }
}

Semua diaturSubviews masih menerima sentuhan, tetapi sentuhan pada UIStackView itu sendiri pergi ke tampilan di bawah (bagi saya sebuah mapView).


Hanya ingin menambahkan Anda dapat menerapkan ini dengan memperluas UIView juga. Berikut adalah artikel yang menggunakan metode yang sama persis yang menjelaskan sedikit lebih detail serta kasus penggunaan lainnya: medium.com/@nguyenminhphuc/…
Kacy

4

Di Swift 5

class ThroughView: UIView {
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        guard let slideView = subviews.first else {
            return false
        }

        return slideView.hitTest(convert(point, to: slideView), with: event) != nil
    }
}

2

Sepertinya meskipun Anda memiliki cukup banyak jawaban di sini, tidak ada yang bersih secepat yang saya butuhkan. Jadi saya mengambil jawaban dari @fresidue di sini dan mengubahnya menjadi cepat karena itulah yang sebagian besar pengembang ingin gunakan di sini sekarang.

Ini memecahkan masalah saya di mana saya memiliki beberapa bilah alat transparan dengan tombol tetapi saya ingin bilah alat tidak terlihat oleh pengguna dan acara sentuh harus dilalui.

isUserInteractionEnabled = false karena beberapa yang dinyatakan bukan merupakan opsi berdasarkan pengujian saya.

 override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    for subview in subviews {
        if subview.hitTest(convert(point, to: subview), with: event) != nil {
            return true
        }
    }
    return false
}

1

Saya memiliki beberapa label di dalam StackView dan saya tidak terlalu berhasil dengan solusi di atas, sebagai gantinya saya memecahkan masalah saya menggunakan kode di bawah ini:

    let item = ResponsiveLabel()

    // Configure label

    stackView.addArrangedSubview(item)

Subclassing UIStackView:

class PassThrouStackView:UIStackView{
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        for subview in self.arrangedSubviews {
            let convertedPoint = convert(point, to: subview)
            let labelPoint = subview.point(inside: convertedPoint, with: event)
            if (labelPoint){
                return subview
            }

        }
        return nil
    }

}

Kemudian Anda dapat melakukan sesuatu seperti:

class ResponsiveLabel:UILabel{
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // Respond to touch
    }
}

0

Coba sesuatu seperti ini ...

for (UIView *view in subviews)
  [view touchesBegan:touches withEvent:event];

Kode di atas, dalam metode touchesBegan Anda misalnya, akan meneruskan sentuhan ke semua tampilan tampilan.


6
Masalah dengan pendekatan AFAIK ini adalah bahwa upaya untuk meneruskan sentuhan ke kelas kerangka UIKit paling sering tidak akan berpengaruh. Menurut dokumentasi Apple, kelas kerangka kerja UIKit tidak dirancang untuk menerima sentuhan yang mengatur properti tampilan ke tampilan lain. Jadi pendekatan ini sebagian besar berfungsi untuk subclass kustom dari UIView.
maciejs

0

Situasi yang saya coba lakukan adalah membangun panel kontrol menggunakan kontrol di dalam UIStackView yang bersarang. Beberapa kontrol memiliki UITextField yang lain dengan UIButton. Juga, ada label untuk mengidentifikasi kontrol tersebut. Yang ingin saya lakukan adalah meletakkan tombol "tidak terlihat" besar di belakang panel kontrol sehingga jika pengguna mengetuk area di luar tombol atau bidang teks, saya kemudian dapat menangkapnya dan mengambil tindakan - terutama menutup keyboard apa pun jika ada teks bidang aktif (resignFirstResponder). Namun, mengetuk label atau area kosong lainnya di panel kontrol tidak akan melewati semuanya. Diskusi di atas sangat membantu dalam menghasilkan jawaban saya di bawah ini.

Pada dasarnya, saya mengelompokkan UIStackView dan menimpa rutin "titik (di dalam: dengan) untuk mencari jenis kontrol yang memerlukan sentuhan dan" mengabaikan "hal-hal seperti label yang ingin saya abaikan. Ia juga memeriksa di dalam UIStackView sehingga hal-hal dapat terjadi kembali ke dalam struktur panel kontrol.

Kode ini mungkin sedikit lebih bertele-tele dari yang seharusnya. Tapi itu membantu dalam debugging dan mudah-mudahan memberikan lebih banyak kejelasan tentang apa yang rutin dilakukan. Pastikan di Interface Builder untuk mengubah kelas UIStackView menjadi PassThruStack.

class PassThruStack: UIStackView {

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {

    for view in self.subviews {
        if !view.isHidden {
            let isStack = view is UIStackView
            let isButton = view is UIButton
            let isText = view is UITextField
            if isStack || isButton || isText {
                let pointInside = view.point(inside: self.convert(point, to: view), with: event)
                if pointInside {
                    return true
                }
            }
        }
    }
    return false
}

}


-1

Seperti yang disarankan oleh @PixelCloudStv jika Anda ingin membuang sentuhan dari satu tampilan ke tampilan lain tetapi dengan beberapa kontrol tambahan atas proses ini - subclass UIView

// header

@interface TouchView : UIView

@property (assign, nonatomic) CGRect activeRect;

@end

//penerapan

#import "TouchView.h"

@implementation TouchView

#pragma mark - Ovverride

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    BOOL moveTouch = YES;
    if (CGRectContainsPoint(self.activeRect, point)) {
        moveTouch = NO;
    }
    return moveTouch;
}

@end

Setelah di interfaceBuilder cukup atur kelas View ke TouchView dan atur kotak aktif dengan kotak Anda. Anda juga dapat mengubah dan menerapkan logika lainnya.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.