Ermöglichen der Interaktion mit einem UIView unter einem anderen UIView


115

Gibt es eine einfache Möglichkeit, die Interaktion mit einer Schaltfläche in einer UIView zuzulassen, die sich unter einer anderen UIView befindet - wo sich keine tatsächlichen Objekte aus der oberen UIView über der Schaltfläche befinden?

Zum Beispiel habe ich im Moment eine UIView (A) mit einem Objekt oben und einem Objekt unten auf dem Bildschirm und nichts in der Mitte. Diese befindet sich auf einer anderen UIView mit Schaltflächen in der Mitte (B). Ich kann jedoch nicht mit den Schaltflächen in der Mitte von B interagieren.

Ich kann die Schaltflächen in B sehen - ich habe den Hintergrund von A auf clearColor gesetzt -, aber die Schaltflächen in B scheinen keine Berührungen zu erhalten, obwohl sich keine Objekte von A tatsächlich über diesen Schaltflächen befinden.

BEARBEITEN - Ich möchte weiterhin in der Lage sein, mit den Objekten in der oberen UIView zu interagieren

Sicher gibt es einen einfachen Weg, dies zu tun?


2
Hier wird so ziemlich alles erklärt: developer.apple.com/iphone/library/documentation/iPhone/… Aber überschreiben Sie im Grunde hitTest: withEvent:, sie liefern sogar ein Codebeispiel.
Nash

Ich habe nur dafür eine kleine Klasse geschrieben. (Ein Beispiel in den Antworten hinzugefügt). Die Lösung dort ist etwas besser als die akzeptierte Antwort, da Sie immer noch auf eine klicken können UIButton, die sich unter einer halbtransparenten befindet, UIViewwährend der nicht transparente Teil der UIViewweiterhin auf Berührungsereignisse reagiert.
Segev

Antworten:


97

Sie sollten eine UIView-Unterklasse für Ihre Draufsicht erstellen und die folgende Methode überschreiben:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    // UIView will be "transparent" for touch events if we return NO
    return (point.y < MIDDLE_Y1 || point.y > MIDDLE_Y2);
}

Sie können sich auch die Methode hitTest: event: ansehen.


19
Was bedeutet middle_y1 / y2 hier?
Jason Renaldo

Ich bin mir nicht sicher, was diese returnAussage tut, aber sie return CGRectContainsPoint(eachSubview.frame, point)funktioniert für mich.
Sehr

Das Geschäft MIDDLE_Y1 / Y2 ist nur ein Beispiel. Diese Funktion ist für Berührungsereignisse in der MIDDLE_Y1<=y<=MIDDLE_Y2Umgebung "transparent" .
Gyim

41

Obwohl viele der Antworten hier funktionieren werden, bin ich ein wenig überrascht zu sehen, dass die bequemste, allgemeinste und narrensicherste Antwort hier nicht gegeben wurde. @Ash kam am nächsten, außer dass mit der Rückgabe der Übersicht etwas Seltsames los ist ... tu das nicht.

Diese Antwort stammt aus einer Antwort, die ich hier auf eine ähnliche Frage gegeben habe .

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];
    if (hitView == self) return nil;
    return hitView;
}

[super hitTest:point withEvent:event]gibt die tiefste Ansicht in der Hierarchie dieser Ansicht zurück, die berührt wurde. Wenn hitView == self(dh wenn sich unter dem Berührungspunkt keine Unteransicht befindet), kehren Sie zurück nilund geben an, dass diese Ansicht die Berührung nicht erhalten soll. Die Funktionsweise der Responderkette bedeutet, dass die Ansichtshierarchie über diesem Punkt weiterhin durchlaufen wird, bis eine Ansicht gefunden wird, die auf die Berührung reagiert. Geben Sie die Übersicht nicht zurück, da es dieser Ansicht nicht überlassen ist, ob die Übersicht Berührungen akzeptieren soll oder nicht!

Diese Lösung ist:

  • praktisch , da keine Verweise auf andere Ansichten / Unteransichten / Objekte erforderlich sind;
  • generisch , da es für jede Ansicht gilt, die lediglich als Container für berührbare Unteransichten fungiert, und die Konfiguration der Unteransichten keinen Einfluss auf die Funktionsweise hat (wie dies der Fall ist, wenn Sie überschreiben pointInside:withEvent:, um einen bestimmten berührbaren Bereich zurückzugeben).
  • narrensicher , es gibt nicht viel Code ... und das Konzept ist nicht schwer zu verstehen.

Ich verwende dies oft genug, um es in eine Unterklasse zu abstrahieren, um sinnlose Ansichtsunterklassen für eine Überschreibung zu speichern. Fügen Sie als Bonus eine Eigenschaft hinzu, um sie konfigurierbar zu machen:

@interface ISView : UIView
@property(nonatomic, assign) BOOL onlyRespondToTouchesInSubviews;
@end

@implementation ISView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];
    if (hitView == self && onlyRespondToTouchesInSubviews) return nil;
    return hitView;
}
@end

Dann gehen Sie wild und verwenden Sie diese Ansicht, wo immer Sie eine Ebene verwenden könnten UIView. Die Konfiguration ist so einfach wie das Einstellen onlyRespondToTouchesInSubviewsauf YES.


2
Die einzig richtige Antwort. Um klar zu sein, wenn Sie Berührungen einer Ansicht ignorieren möchten , aber keine Schaltflächen (z. B.), die in der Ansicht enthalten sind , tun Sie, was Stuart erklärt. (Ich nenne es normalerweise eine "Halteransicht", weil es einige Knöpfe harmlos "halten" kann, aber nichts "unter" dem Halter beeinflusst.)
Fattie

Einige der anderen Lösungen, die Punkte verwenden, haben Probleme, wenn Ihre Ansicht scrollbar ist, z. B. eine UITableView oder eine UICollectionView, und Sie nach oben oder unten gescrollt haben. Diese Lösung funktioniert jedoch unabhängig vom Bildlauf.
pajevic

31

Es gibt verschiedene Möglichkeiten, wie Sie damit umgehen können. Mein Favorit ist es, hitTest: withEvent: in einer Ansicht zu überschreiben, die eine gemeinsame Übersicht (möglicherweise indirekt) zu den widersprüchlichen Ansichten darstellt (klingt so, als würden Sie diese A und B nennen). Zum Beispiel so etwas (hier sind A und B UIView-Zeiger, wobei B der "versteckte" ist, der normalerweise ignoriert wird):

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    CGPoint pointInB = [B convertPoint:point fromView:self];

    if ([B pointInside:pointInB withEvent:event])
        return B;

    return [super hitTest:point withEvent:event];
}

Sie können die pointInside:withEvent:Methode auch wie von Gyim vorgeschlagen ändern. Auf diese Weise können Sie im Wesentlichen das gleiche Ergebnis erzielen, indem Sie zumindest bei Berührungen effektiv ein Loch in A "stechen".

Ein anderer Ansatz ist die Ereignisweiterleitung, dh das Überschreiben touchesBegan:withEvent:und ähnliche Methoden (wie touchesMoved:withEvent:usw.), um einige Berührungen an ein anderes Objekt zu senden, als wenn sie zuerst ausgeführt werden. In A könnten Sie beispielsweise Folgendes schreiben:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([self shouldForwardTouches:touches]) {
        [B touchesBegan:touches withEvent:event];
    }
    else {
        // Do whatever A does with touches.
    }
}

Dies funktioniert jedoch nicht immer so, wie Sie es erwarten! Die Hauptsache ist, dass eingebaute Steuerelemente wie UIButton weitergeleitete Berührungen immer ignorieren. Aus diesem Grund ist der erste Ansatz zuverlässiger.

Es gibt einen guten Blog-Beitrag, der dies alles ausführlicher erklärt, zusammen mit einem kleinen funktionierenden Xcode-Projekt, um die Ideen zu demonstrieren, das hier verfügbar ist:

http://bynomial.com/blog/?p=74


Das Problem bei dieser Lösung - das Überschreiben von hitTest in der allgemeinen Übersicht - besteht darin, dass die Unteransicht nicht vollständig korrekt funktioniert, wenn die Unteransicht Teil einer ganzen Reihe von scrollbaren Ansichten (MapView usw.) ist. Das Überschreiben von pointInside in der Draufsicht, wie von Gyim unten vorgeschlagen, funktioniert in allen Fällen, soweit ich das beurteilen kann.
Delany

@ Delany, das stimmt nicht; Sie können scrollbare Ansichten unter anderen Ansichten haben und beide arbeiten lassen, indem Sie hitTest überschreiben. Hier ist ein Beispielcode: bynomial.com/blogfiles/Temp32.zip
Tyler

Hallo. Nicht alle scrollbaren Ansichten - nur einige ... Ich habe Ihre Postleitzahl von oben ausprobiert und die UIScrollView in eine (zum Beispiel) MKMapView geändert, und sie funktioniert nicht. Taps funktionieren - es ist das Scrollen, das das Problem zu sein scheint.
Delany

Ok, ich habe es überprüft und bestätigt, dass hitTest mit MKMapView nicht so funktioniert, wie Sie es möchten. Da hatten Sie recht, @delany; obwohl es mit UIScrollViews richtig funktioniert. Ich frage mich, warum MKMapView fehlschlägt.
Tyler

@ Tyler Wie Sie bereits erwähnt haben: "Die Hauptsache ist, dass integrierte Steuerelemente wie UIButton weitergeleitete Berührungen immer ignorieren." Ich möchte wissen, woher Sie das wissen und ob es sich um ein offizielles Dokument handelt, um dieses Verhalten zu erklären. Ich hatte das Problem, dass UIButton bei einer einfachen UIView-Unteransicht nicht auf Berührungsereignisse in den Grenzen der Unteransicht reagiert. Ich habe festgestellt, dass das Ereignis ordnungsgemäß als Standardverhalten von UIView an den UIButton weitergeleitet wird, bin mir jedoch nicht sicher, ob es sich um eine bestimmte Funktion oder nur um einen Fehler handelt. Würden Sie mir bitte die Dokumentation dazu zeigen? Vielen Dank aufrichtig.
Neal.Marlin

29

Sie müssen einstellen upperView.userInteractionEnabled = NO;, sonst fängt die obere Ansicht die Berührungen ab.

Die Interface Builder-Version ist ein Kontrollkästchen am unteren Rand des Bedienfelds "Ansichtsattribute" mit dem Namen "Benutzerinteraktion aktiviert". Deaktivieren Sie es und Sie sollten bereit sein zu gehen.


Entschuldigung - hätte sagen sollen. Ich möchte weiterhin in der Lage sein, mit den Objekten in der oberen UIView zu interagieren.
Delany

Die obere Ansicht kann jedoch keine Berührung erhalten. Fügen Sie die Schaltfläche in die obere Ansicht ein.
Imcaptor

2
Diese Lösung funktioniert für mich. Ich brauchte die obere Ansicht überhaupt nicht, um auf Berührungen zu reagieren.
TJ

11

Benutzerdefinierte Implementierung von pointInside: withEvent: schien zwar der richtige Weg zu sein, aber der Umgang mit fest codierten Koordinaten erschien mir seltsam. Also habe ich mit der Funktion CGRectContainsPoint () überprüft, ob sich der CGPoint in der Schaltfläche CGRect befindet:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    return (CGRectContainsPoint(disclosureButton.frame, point));
}

8

In letzter Zeit habe ich eine Klasse geschrieben, die mir dabei helfen wird. Wenn Sie es als benutzerdefinierte Klasse für ein UIButtonoder UIViewmehrere Berührungsereignisse verwenden, die auf einem transparenten Pixel ausgeführt wurden.

Diese Lösung ist etwas besser als die akzeptierte Antwort, da Sie immer noch auf eine Antwort klicken können UIButton, die sich unter einer halbtransparenten befindet, UIViewwährend der nicht transparente Teil der Lösung UIViewweiterhin auf Berührungsereignisse reagiert.

GIF

Wie Sie im GIF sehen können, ist die Giraffenschaltfläche ein einfaches Rechteck, aber Berührungsereignisse in transparenten Bereichen werden an das UIButtondarunter liegende Gelb weitergeleitet .

Link zur Klasse


2
Da Ihr Code nicht so lang ist, sollten Sie die relevanten Teile davon in Ihre Antwort aufnehmen, falls Ihr Projekt zu einem späteren Zeitpunkt verschoben oder entfernt wird.
Gavin

Vielen Dank, Ihre Lösung funktioniert für meinen Fall, in dem ich den nicht transparenten Teil meines UIView benötige, um auf Berührungsereignisse zu reagieren, während der transparente Teil dies nicht tut. Brillant!
Bruce

@ Bruce Ich bin froh, dass es dir geholfen hat!
Segev

4

Ich glaube, ich bin etwas spät dran, aber ich werde diese mögliche Lösung hinzufügen:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *hitView = [super hitTest:point withEvent:event];
    if (hitView != self) return hitView;
    return [self superview];
}

Wenn Sie diesen Code verwenden, um die Standard-HitTest-Funktion eines benutzerdefinierten UIView zu überschreiben, wird NUR die Ansicht selbst ignoriert. Alle Unteransichten dieser Ansicht geben ihre Treffer normal zurück, und alle Treffer, die in die Ansicht selbst gelangt wären, werden an ihre Übersicht weitergeleitet.

-Asche


1
Dies ist meine bevorzugte Methode, außer ich denke nicht, dass Sie zurückkehren sollten [self superview]. In der Dokumentation zu dieser Methode heißt es: "Gibt den am weitesten entfernten Nachkommen des Empfängers in der Ansichtshierarchie (einschließlich sich selbst) zurück, der einen bestimmten Punkt enthält" und "Gibt null zurück, wenn der Punkt vollständig außerhalb der Ansichtshierarchie des Empfängers liegt". Ich denke, Sie sollten zurückkehren nil. Wenn Sie nil zurückgeben, wird die Steuerung an die Übersicht übergeben, um zu überprüfen, ob Treffer vorhanden sind oder nicht. Im Grunde wird es das Gleiche tun, außer dass die Rückkehr der Übersicht in Zukunft etwas kaputt machen könnte.
Jasongregori

Sicher, das wäre wahrscheinlich klug (notieren Sie sich das Datum auf meiner ursprünglichen Antwort - seitdem viel mehr codiert)
Ash

4

Ich habe nur die akzeptierte Antwort durchgesehen und sie hier als Referenz eingefügt. Die akzeptierte Antwort funktioniert einwandfrei. Sie können es folgendermaßen erweitern, damit die Unteransichten Ihrer Ansicht die Berührung erhalten, ODER es an alle Ansichten hinter uns weitergeben:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    // If one of our subviews wants it, return YES
    for (UIView *subview in self.subviews) {
        CGPoint pointInSubview = [subview convertPoint:point fromView:self];
        if ([subview pointInside:pointInSubview withEvent:event]) {
            return YES;
        }
    }
    // otherwise return NO, as if userInteractionEnabled were NO
    return NO;
}

Hinweis: Sie müssen nicht einmal eine Rekursion für den Unteransichtsbaum durchführen, da jede pointInside:withEvent:Methode dies für Sie erledigt .


3

Das Deaktivieren der Eigenschaft userInteraction kann hilfreich sein. Z.B:

UIView * topView = [[TOPView alloc] initWithFrame:[self bounds]];
[self addSubview:topView];
[topView setUserInteractionEnabled:NO];

(Hinweis: Im obigen Code bezieht sich 'self' auf eine Ansicht.)

Auf diese Weise können Sie nur in der topView anzeigen, erhalten jedoch keine Benutzereingaben. Alle diese Benutzerberührungen werden durch diese Ansicht und die Unteransicht wird für sie antworten. Ich würde diese topView verwenden, um transparente Bilder anzuzeigen oder sie zu animieren.


3

Dieser Ansatz ist recht sauber und ermöglicht, dass transparente Unteransichten nicht auch auf Berührungen reagieren. Unterklasse einfach UIViewund füge der Implementierung die folgende Methode hinzu:

@implementation PassThroughUIView

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    for (UIView *v in self.subviews) {
        CGPoint localPoint = [v convertPoint:point fromView:self];
        if (v.alpha > 0.01 && ![v isHidden] && v.userInteractionEnabled && [v pointInside:localPoint withEvent:event])
            return YES;
    }
    return NO;
}

@end

2

Meine Lösung hier:

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    CGPoint pointInView = [self.toolkitController.toolbar convertPoint:point fromView:self];

    if ([self.toolkitController.toolbar pointInside:pointInView withEvent:event]) {
       self.userInteractionEnabled = YES;
    } else {
       self.userInteractionEnabled = NO;
    }

    return [super hitTest:point withEvent:event];
}

Hoffe das hilft


2

In beiden Ansichten können Sie die Berührung abfangen.

Draufsicht:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   // Do code in the top view
   [bottomView touchesBegan:touches withEvent:event]; // And pass them on to bottomView
   // You have to implement the code for touchesBegan, touchesEnded, touchesCancelled in top/bottom view.
}

Aber das ist die Idee.


Dies ist sicherlich möglich - aber es ist eine Menge Arbeit (Sie müssten Ihre eigenen berührungsempfindlichen Objekte in der untersten Ebene rollen (z. B. Schaltflächen), denke ich?) Und es scheint seltsam, dass Sie Ihre eigenen darin rollen müssten Weg, um Verhalten zu bekommen, das intuitiv zu sein scheint.
Delany

Ich bin mir nicht sicher, ob wir es vielleicht einfach versuchen sollten.
Alexandre Cassagne

2

Hier ist eine Swift-Version:

override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
    return !CGRectContainsPoint(buttonView.frame, point)
}

2

Swift 3

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    for subview in subviews {
        if subview.frame.contains(point) {
            return true
        }
    }
    return false
}

1

Ich habe noch nie eine vollständige Benutzeroberfläche mit dem UI-Toolkit erstellt, daher habe ich nicht viel Erfahrung damit. Hier ist, was ich denke, sollte funktionieren.

Jedes UIView und das UIWindow hat eine Eigenschaft subviews , bei der es sich um ein NSArray handelt, das alle Unteransichten enthält.

Die erste Unteransicht, die Sie einer Ansicht hinzufügen, erhält den Index 0 und den nächsten Index 1 usw. Sie können auch ersetzen addSubview:mit insertSubview: atIndex:oderinsertSubview:aboveSubview: und solche Methoden , die die Position Ihrer Subview in der Hierarchie bestimmen kann.

Überprüfen Sie also Ihren Code, um festzustellen, welche Ansicht Sie zuerst zu Ihrem UIWindow hinzufügen. Das ist 0, das andere ist 1.
Nun würden Sie in einer Ihrer Unteransichten Folgendes tun, um eine andere zu erreichen:

UIView * theOtherView = [[[self superview] subviews] objectAtIndex: 0];
// or using the properties syntax
UIView * theOtherView = [self.superview.subviews objectAtIndex:0];

Lassen Sie mich wissen, ob das für Ihren Fall funktioniert!


(Unter diesem Marker befindet sich meine vorherige Antwort):

Wenn Ansichten miteinander kommunizieren müssen, sollten sie dies über einen Controller tun ( dh mithilfe der beliebten MVC) Modells).

Wenn Sie eine neue Ansicht erstellen, können Sie sicherstellen, dass sie sich bei einem Controller registriert.

Die Technik besteht also darin, sicherzustellen, dass sich Ihre Ansichten bei einem Controller registrieren (der sie nach Namen oder nach Belieben in einem Wörterbuch oder Array speichern kann). Entweder kann der Controller eine Nachricht für Sie senden, oder Sie können einen Verweis auf die Ansicht abrufen und direkt mit ihr kommunizieren.

Wenn Ihre Ansicht keinen Link zum Controller enthält (was möglicherweise der Fall ist), können Sie Singletons und / oder Klassenmethoden verwenden, um einen Verweis auf Ihren Controller zu erhalten.


Vielen Dank für Ihre Antwort - aber ich bin nicht sicher, ob ich das verstehe. Beide Ansichten haben einen Controller - das Problem ist, dass bei einer Ansicht über der anderen die untere Ansicht keine Ereignisse aufnimmt (und sie an den Controller weiterleitet), selbst wenn kein Objekt diese Ereignisse tatsächlich blockiert die Draufsicht.
Delany

Haben Sie ein UIWindow oben in unseren UIViews? Wenn Sie dies tun, sollten die Ereignisse weitergegeben werden und Sie sollten keine "Magie" ausführen müssen. Lesen Sie mehr über [Fenster und Ansichten] [1] im Apple Dev Center (und fügen Sie auf jeden Fall einen weiteren Kommentar hinzu, wenn Ihnen dies nicht hilft!) [1]: developer.apple.com/iphone/library/documentation/ iPhone /…
Nash

Ja, absolut - ein UIWindow an der Spitze der Hierarchie.
Delany

Ich habe meine Antwort so aktualisiert, dass sie den Code zum Durchlaufen einer Hierarchie von Ansichten enthält. Lassen Sie mich wissen, wenn Sie weitere Hilfe benötigen!
Nash

Vielen Dank für Ihre Hilfe - aber ich bin ziemlich erfahren mit dem Erstellen dieser Schnittstellen und meine aktuelle ist meines Wissens korrekt eingerichtet. Das Problem scheint das Standardverhalten von Vollansichten zu sein, wenn sie über anderen platziert werden.
Delany

1

Ich denke, der richtige Weg ist, die in die Ansichtshierarchie integrierte Ansichtskette zu verwenden. Verwenden Sie für Ihre Unteransichten, die in die Hauptansicht verschoben werden, nicht die generische UIView, sondern die Unterklasse UIView (oder eine ihrer Varianten wie UIImageView), um MYView: UIView (oder einen beliebigen Supertyp wie UIImageView) zu erstellen. Implementieren Sie in der Implementierung für YourView die touchBegan-Methode. Diese Methode wird dann aufgerufen, wenn diese Ansicht berührt wird. Alles, was Sie in dieser Implementierung benötigen, ist eine Instanzmethode:

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event ;
{   // cannot handle this event. pass off to super
    [self.superview touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event]; }

Diese BerührungBegan ist eine Responder-API, sodass Sie sie nicht in Ihrer öffentlichen oder privaten Benutzeroberfläche deklarieren müssen. Es ist eine dieser magischen APIs, über die Sie nur Bescheid wissen müssen. Diese self.superview sprudelt die Anfrage schließlich an den viewController. Implementieren Sie dann im viewController diesen touchBegan, um die Berührung zu verarbeiten.

Beachten Sie, dass die Berührungsposition (CGPoint) automatisch relativ zur umfassenden Ansicht für Sie angepasst wird, wenn sie in der Ansichtshierarchiekette zurückgeworfen wird.


1

Ich möchte dies nur posten, da ich ein ähnliches Problem hatte und viel Zeit damit verbracht habe, Antworten hier ohne Glück umzusetzen. Was ich letztendlich gemacht habe:

 for(UIGestureRecognizer *recognizer in topView.gestureRecognizers)
 {
     recognizer.delegate=self;
     [bottomView addGestureRecognizer:recognizer];   
 }
 topView.abView.userInteractionEnabled=NO; 

und Umsetzung UIGestureRecognizerDelegate:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

Die Ansicht von unten war ein Navigationscontroller mit einer Anzahl von Abschnitten, und ich hatte eine Art Tür darüber, die sich mit einer Schwenkgeste schließen konnte. Das Ganze war in eine weitere VC eingebettet. Lief wie am Schnürchen. Hoffe das hilft.


1

Swift 4-Implementierung für HitTest-basierte Lösung

let hitView = super.hitTest(point, with: event)
if hitView == self { return nil }
return hitView

0

Abgeleitet von Stuarts hervorragender und größtenteils narrensicherer Antwort und Segevs nützlicher Implementierung ist hier ein Swift 4-Paket, das Sie in jedes Projekt einbinden können:

extension UIColor {
    static func colorOfPoint(point:CGPoint, in view: UIView) -> UIColor {

        var pixel: [CUnsignedChar] = [0, 0, 0, 0]

        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)

        let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)

        context!.translateBy(x: -point.x, y: -point.y)

        view.layer.render(in: context!)

        let red: CGFloat   = CGFloat(pixel[0]) / 255.0
        let green: CGFloat = CGFloat(pixel[1]) / 255.0
        let blue: CGFloat  = CGFloat(pixel[2]) / 255.0
        let alpha: CGFloat = CGFloat(pixel[3]) / 255.0

        let color = UIColor(red:red, green: green, blue:blue, alpha:alpha)

        return color
    }
}

Und dann mit hitTest:

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    guard UIColor.colorOfPoint(point: point, in: self).cgColor.alpha > 0 else { return nil }
    return super.hitTest(point, with: event)
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.