iOS - tüm dokunuşları bir görünüm üzerinden iletin


141

Diğer görünümlerin üstüne bindirilmiş bir görünüm var. Sadece ekranda bazı dokunuşları tespit etmek için çok fazla kullanıyorum, ancak bunun dışında, görünümün altındaki diğer görünümlerin davranışlarını durdurmasını istemiyorum, bu da kaydırma görüntülerini vb. bu bindirme görünümü? UIView'in bir alt hesaplamasıdır.


2
Sorununuzu çözdünüz mü? Cevabınız evet ise, lütfen aynı sorunu karşıladığım için başkalarının çözümü bulması daha kolay olacaktır.
Khushbu Desai

Yanıtlar:


121

Tek ihtiyacım olan kullanıcı etkileşimini devre dışı bırakmak!

Objective-C:

myWebView.userInteractionEnabled = NO;

Swift:

myWebView.isUserInteractionEnabled = false

Bu, düğmenin bir alt görünümünü aldığım durumumda çalıştı. Alt görünümde etkileşimi devre dışı bıraktım ve düğme çalıştı.
simple_code

2
Swift 4'temyWebView.isUserInteractionEnabled = false
Louis 'LYRO' Dupont

117

Bir bindirme görünümünden alttaki görünümlere dokunmak için, UIView'da aşağıdaki yöntemi uygulayın:

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

Hızlı 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
}

3
Ben de aynı sorun var, ama ben hareket ile yakınlaştırma / döndürme / hareketli görünüm varsa o zaman evet dönmelidir ve dinlenme olay için hayır dönmelidir, bunu nasıl yapabilirim?
Minakshi

@ Minakshi bu tamamen farklı bir soru, bunun için yeni bir soru sor.
Fattie

ancak bu basit yaklaşımın söz konusu katmanın ÜSTÜNDE düğmelere ve benzeri özelliklere sahip OLMAYINIZ!
Fattie

56

Bu eski bir iş parçacığı, ama bir arama geldi, bu yüzden benim 2c eklemek düşündüm. Alt görüntülemeleri içeren bir UIView'im var ve yalnızca alt görünümlerden birine çarpan dokunuşları durdurmak istiyorum, bu yüzden PixelCloudSt'nin cevabını değiştirdim

-(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
Özel bir UIView alt sınıfı (veya UIImageView veya başka herhangi bir UIView alt sınıfının bir alt sınıfı) oluşturmanız ve bu işlevi geçersiz kılmanız gerekir. Özünde, alt sınıf .h dosyasında tamamen boş bir '@interface' içerebilir ve yukarıdaki fonksiyon '@implementation' içeriğinin tamamı olabilir.
fresidue

Teşekkürler, tam olarak UIView'imin boş alanından dokunarak geçmek için ihtiyacım olan şey bu. +100
Danny

41

@Fresidue yanıtının geliştirilmiş sürümü. Bu UIViewalt sınıfı, alt görünümü dışında saydam görünüm geçiş dokunuşları olarak kullanabilirsiniz. Amaç-C'de Uygulama :

@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

.

ve Swift'te:

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

İPUCU:

Diyelim ki büyük bir “tutucu” paneliniz var, belki de arkasında bir tablo görünümü var. "Tutucu" paneli yaparsınız PassthroughView. Şimdi çalışacak, tabloyu "" tutucu "boyunca kaydırabilirsiniz.

Fakat!

  1. "Tutucu" panelin üstünde bazı etiketler veya simgeler bulunur. Tabii ki bunlar sadece kullanıcı etkileşimi etkin olarak işaretlenmiş olmalıdır!

  2. "Tutucu" panelin üstünde bazı düğmeler bulunur. Unutmayın, elbette bunlar işaretlenmiş kullanıcı etkileşimi AÇIK olarak işaretlenmelidir!

  3. Not kullandığınız görünümü - biraz karışıklığa, "tutucu" kendisi olduğunu PassthroughViewüzerine - işaretlenmiş olmalıdır kullanıcı etkileşimi etkin ON ! Budur AÇIK !! (Aksi takdirde, PassthroughView içindeki kod asla çağrılmaz.)


1
Swift için verilen çözümü kullandım. IOS 11 üzerinde çalışıyor ancak iOS 10'da her seferinde çöküyor. Kötü Erişim Gösteriliyor. Herhangi bir fikir?
Soumen

Günlerimi kurtardın, aşağıdaki UIViews'a dokunuşlar için işe yaradı.
AaoIi

1
Ayrıca kontrol etmeyi unutma$0.isUserInteractionEnabled
Sean Thielen

7

Bir UIStackView ile benzer bir sorun vardı (ama başka bir görünüm olabilir). Yapılandırmam şuydu: ContainerView ve StackView içeren görünüm denetleyicisi

Bu, arka tarafa yerleştirilmesi gereken bir konteynerin olduğu, yandaki düğmelerle klasik bir durum. Düzen amacıyla, düğmeleri bir UIStackView'e dahil ettim, ancak şimdi stackView kesişmelerinin orta (boş) kısmı dokunuyor :-(

Yaptığım, dokunmatik olması gereken subView'u tanımlayan bir özelliğe sahip bir UIStackView alt sınıfı oluşturmaktır. Şimdi, yan düğmelerdeki herhangi bir dokunuş (* viewsWithActiveTouch * dizisine dahil) düğmelere verilecekken, bu görünümler dışındaki herhangi bir yerde yığın görünümüne herhangi bir dokunma engellenmeyecek ve bu nedenle yığının altındaki herhangi bir şeye geçmeyecektir. görünüm.

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

Dokunuşları iletmek istediğiniz görünüm bir alt görüntüleme / denetleme olmazsa, UIView alt sınıfınızda aşağıdaki gibi özel bir özellik ayarlayabilirsiniz:

@interface SomeViewSubclass : UIView {

    id forwardableTouchee;

}
@property (retain) id forwardableTouchee;

.M dosyasında sentezlediğinizden emin olun:

@synthesize forwardableTouchee;

Ve sonra aşağıdakiler gibi UIResponder yöntemlerinize aşağıdakileri ekleyin:

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

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

}

UIView'inizi nereden başlatırsanız yapın, forwardableTouchee özelliğini, olayların yönlendirilmesini istediğiniz görünüme ayarlayın:

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

4

Swift 5'te

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

4

Dokunuşları bir UIStackView üzerinden geçirmem gerekiyordu. İçerideki bir UIView şeffaftı, ancak UIStackView tüm dokunuşları tüketti. Bu benim için çalıştı:

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

Tüm düzenlenmişSubviews hala dokunuşlar alıyor, ancak UIStackView üzerindeki dokunuşlar aşağıdaki görünüme geçti (benim için bir mapView).


2

Görünüşe göre burada oldukça fazla cevap var, ihtiyaç duyduğum çabuk temiz kimse yok. Bu yüzden @fresidue'dan cevap aldım ve şimdi çoğunlukla geliştiricilerin burada kullanmak istediği gibi hızlı bir şekilde dönüştürdüm.

Düğmeyle şeffaf bir araç çubuğum olduğu zaman sorunumu çözdüm, ancak araç çubuğunun kullanıcı için görünmez olmasını ve dokunma olaylarının geçmesini istiyorum.

isUserInteractionEnabled = false, bazıları belirtildiği gibi testlerime dayalı bir seçenek değil.

 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

StackView içinde birkaç etiket vardı ve yukarıdaki çözümler ile çok başarılı olmadı, bunun yerine aşağıdaki kodu kullanarak sorunumu çözdü:

    let item = ResponsiveLabel()

    // Configure label

    stackView.addArrangedSubview(item)

Alt sınıflandırma 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
    }

}

Sonra şöyle bir şey yapabilirsiniz:

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

0

Böyle bir şey deneyin ...

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

Yukarıdaki kod, touchesBegan yönteminizde, dokunuşları tüm görünüm alt görünümlerine geçirir.


6
Bu yaklaşımla ilgili sorun AFAIK, UIKit çerçeve sınıflarına dokunma girişiminde bulunmanın çoğunlukla bir etkisi olmayacağıdır. Apple'ın belgelerine göre UIKit çerçeve sınıfları, view özelliği başka bir görünüme ayarlanmış dokunuşları almak için tasarlanmamıştır. Dolayısıyla bu yaklaşım çoğunlukla UIView'in özel alt sınıfları için geçerlidir.
maciejs

0

Yapmaya çalıştığım durum, iç içe UIStackView'ın içindeki denetimleri kullanarak bir kontrol paneli oluşturmaktı. Bazı kontroller UITextField'ın UIButton'la diğerlerine sahipti. Ayrıca, kontrolleri tanımlayan etiketler de vardı. Yapmak istediğim şey, kontrol panelinin arkasına büyük bir "görünmez" düğme koymaktı, böylece bir kullanıcı bir düğmenin veya metin alanının dışındaki bir alana dokunduysa, bunu yakalayabilir ve harekete geçebilirim - bir metin varsa öncelikle herhangi bir klavyeyi devre dışı bırakabilirim alanı etkinti (resignFirstResponder). Bununla birlikte, kontrol panelindeki bir etikete veya başka bir boş alana dokunmak hiçbir şey yapmaz. Yukarıdaki tartışmalar aşağıdaki cevabımı bulmakta yardımcı oldu.

Temel olarak, UIStackView alt sınıf ve ben dokunma ihtiyacı ve yoksaymak istediğim etiketler gibi şeyleri "yoksay" kontrol türünü aramak için "nokta (içinde: ile) rutin üzerine yazdım. Ayrıca, UIStackView'ın içini kontrol eder, böylece her şey kontrol paneli yapısına geri dönebilir.

Kod belki olması gerekenden biraz daha ayrıntılıdır. Ancak hata ayıklamada yardımcı oldu ve umarım rutinin ne yaptığı konusunda daha fazla netlik sağlar. Arabirim Oluşturucu'da, UIStackView öğelerinin sınıfını PassThruStack olarak değiştirdiğinizden emin olun.

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

@PixelCloudStv tarafından önerildiği gibi, bir görünümden diğerine dokunmak istiyorsanız, ancak bu işlem üzerinde ek kontrol ile - alt sınıf UIView

// başlık

@interface TouchView : UIView

@property (assign, nonatomic) CGRect activeRect;

@end

// uygulama

#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

İnterfaceBuilder'dan sonra View sınıfını TouchView olarak ayarlayın ve rektinizle aktif rect ayarlayın. Ayrıca u değiştirebilir ve diğer mantığı uygulayabilirsiniz.

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.