Umożliwienie interakcji z UIView w ramach innego UIView

115

Czy istnieje prosty sposób na umożliwienie interakcji z przyciskiem w UIView, który znajduje się pod innym UIView - gdzie nie ma żadnych rzeczywistych obiektów z górnego UIView na przycisku?

Na przykład w tej chwili mam UIView (A) z obiektem na górze i obiektem na dole ekranu i niczym na środku. Znajduje się on na innym UIView, który ma przyciski pośrodku (B). Jednak nie wydaje mi się, żebym wchodził w interakcję z przyciskami w środku B.

Widzę przyciski w B - ustawiłem tło A na clearColor - ale wydaje się, że przyciski w B nie są dotknięte, mimo że na górze tych przycisków nie ma żadnych obiektów z A.

EDYTUJ - nadal chcę mieć możliwość interakcji z obiektami w górnym UIView

Z pewnością jest na to prosty sposób?

delany
źródło
2
Prawie wszystko zostało wyjaśnione tutaj: developer.apple.com/iphone/library/documentation/iPhone/ ... Ale w zasadzie zastąp hitTest: withEvent :, dostarczają nawet próbkę kodu.
nash
Właśnie w tym celu napisałem małą klasę. (Dodano przykład w odpowiedziach). Rozwiązanie jest nieco lepsze niż zaakceptowana odpowiedź, ponieważ nadal można kliknąć, UIButtonktóra jest pod półprzezroczystą, UIViewpodczas gdy nieprzezroczysta część UIViewbędzie nadal reagować na zdarzenia dotykowe.
Segev

Odpowiedzi:

97

Powinieneś utworzyć podklasę UIView dla swojego widoku z góry i zastąpić następującą metodę:

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

Możesz również spojrzeć na metodę hitTest: event:.

gyim
źródło
19
Co tutaj reprezentuje middle_y1 / y2?
Jason Renaldo
Nie jestem pewien, co returnrobi to stwierdzenie, ale return CGRectContainsPoint(eachSubview.frame, point)działa dla mnie. Niezwykle pomocna odpowiedź w przeciwnym razie
n00neimp0rtant
Biznes MIDDLE_Y1 / Y2 to tylko przykład. Ta funkcja będzie „przezroczysta” dla zdarzeń dotykowych w MIDDLE_Y1<=y<=MIDDLE_Y2okolicy.
gyim
41

Chociaż wiele odpowiedzi tutaj zadziała, jestem trochę zaskoczony, widząc, że nie podano tutaj najwygodniejszej, ogólnej i niezawodnej odpowiedzi. @Ash był najbliżej, z tym wyjątkiem, że dzieje się coś dziwnego z powrotem superwizji ... nie rób tego.

Ta odpowiedź jest pobierana z odpowiedzi dałem podobnym pytaniem, tutaj .

- (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]zwróci najgłębszy widok w hierarchii tego widoku, który został dotknięty. Jeśli hitView == self(tj. Jeśli pod punktem dotyku nie ma widoku podrzędnego), wróć nil, określając, że ten widok nie powinien otrzymać dotknięcia. Sposób działania łańcucha odpowiedzi oznacza, że ​​hierarchia widoków powyżej tego punktu będzie kontynuowana, dopóki nie zostanie znaleziony widok reagujący na dotyk. Nie zwracaj superwizji, ponieważ od tego poglądu nie zależy, czy jej superview powinien akceptować dotknięcia, czy nie!

To rozwiązanie to:

  • wygodne , ponieważ nie wymaga odniesień do żadnych innych widoków / podwidoków / obiektów;
  • generic , ponieważ ma zastosowanie do każdego widoku, który działa wyłącznie jako kontener dla dotykowych podglądów podrzędnych, a konfiguracja podglądów podrzędnych nie wpływa na sposób ich działania (tak jak ma to miejsce w przypadku zastąpienia w pointInside:withEvent:celu zwrócenia określonego obszaru do dotknięcia).
  • niezawodna , nie ma zbyt wiele kodu ... a koncepcja nie jest trudna do zrozumienia.

Używam tego na tyle często, że wyabstrahowałem go do podklasy, aby zapisać podklasy bezcelowego widoku dla jednego zastąpienia. Jako bonus dodaj właściwość, aby była konfigurowalna:

@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

Następnie zaszalej i użyj tego widoku, gdziekolwiek możesz użyć zwykłego UIView. Konfiguracja jest tak proste jak tworzenie onlyRespondToTouchesInSubviewssię YES.

Stuart
źródło
2
Jedyna poprawna odpowiedź. aby było jasne, jeśli chcesz zignorować dotknięcia widoku, ale nie przyciski (powiedzmy) zawarte w widoku , zrób tak, jak wyjaśnia Stuart. (Zwykle nazywam to „widokiem uchwytu”, ponieważ może on nieszkodliwie „przytrzymywać” niektóre przyciski, ale nie wpływa na nic „poniżej” uchwytu.)
Fattie
Niektóre inne rozwiązania wykorzystujące punkty mają problemy, jeśli widok jest przewijalny, na przykład UITableView lub UICollectionView i przewinąłeś w górę lub w dół. To rozwiązanie działa jednak niezależnie od przewijania.
pajevic
31

Możesz sobie z tym poradzić na kilka sposobów. Moim ulubionym jest przesłonięcie hitTest: withEvent: w widoku, który jest wspólnym superwizorem (może pośrednio) na sprzeczne widoki (brzmi to tak, jakbyś nazywał te A i B). Na przykład coś takiego (tutaj A i B to wskaźniki UIView, gdzie B to wskaźnik „ukryty”, który jest zwykle ignorowany):

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

Możesz także zmodyfikować pointInside:withEvent:metodę zgodnie z sugestią gyim. Pozwala to osiągnąć zasadniczo ten sam rezultat, skutecznie „wbijając dziurę” w A, przynajmniej za dotknięcia.

Innym podejściem jest przekazywanie zdarzeń, co oznacza nadpisywanie touchesBegan:withEvent:i podobne metody (takie jak touchesMoved:withEvent:itp.) Do wysyłania niektórych dotknięć do innego obiektu niż ten, do którego trafiły po raz pierwszy. Na przykład w A możesz napisać coś takiego:

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

Jednak nie zawsze będzie to działać tak, jak oczekujesz! Najważniejsze jest to, że wbudowane kontrolki, takie jak UIButton, zawsze ignorują przekazane dotknięcia. Z tego powodu pierwsze podejście jest bardziej niezawodne.

Jest dobry post na blogu wyjaśniający to wszystko bardziej szczegółowo, wraz z małym działającym projektem xcode do demonstracji pomysłów, dostępnym tutaj:

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

Tyler
źródło
Problem z tym rozwiązaniem - przesłanianiem hitTest we wspólnym superviewie - polega na tym, że nie pozwala ono na całkowicie poprawne działanie widoku pod spodem, gdy widok pod spodem jest jednym z całego zestawu przewijalnych widoków (MapView itp.). Punkt nadrzędny: Wnętrze w widoku z góry, jak sugeruje poniższy gyim, działa we wszystkich przypadkach, o ile wiem.
delany,
@delany, to nieprawda; możesz mieć przewijalne widoki siedzące pod innymi widokami i pozwolić im działać, zastępując hitTest. Oto przykładowy kod: bynomial.com/blogfiles/Temp32.zip
Tyler
Hej. Nie wszystkie przewijalne widoki - tylko niektóre ... Próbowałem twojego kodu pocztowego z góry, zmieniając UIScrollView na (na przykład) MKMapView i nie działa. Krany działają - wydaje się, że problemem jest przewijanie.
delany
Ok, sprawdziłem to i potwierdziłem, że hitTest nie działa tak, jak chcesz z MKMapView. Miałeś rację, @delany; chociaż działa poprawnie z plikami UIScrollView. Zastanawiam się, dlaczego MKMapView zawodzi?
Tyler,
@Tyler Jak wspomniałeś, „Najważniejsze jest to, że wbudowane elementy sterujące, takie jak UIButton, zawsze będą ignorować przekazane dotknięcia”. Chcę wiedzieć, skąd to wiesz i czy jest to oficjalny dokument wyjaśniający to zachowanie. Napotkałem problem polegający na tym, że gdy UIButton ma zwykły widok podrzędny UIView, nie będzie reagował na zdarzenia dotykowe w granicach widoku podrzędnego. I odkryłem, że zdarzenie jest poprawnie przekazywane do UIButton jako domyślne zachowanie UIView, ale nie jestem pewien, czy jest to wyznaczona funkcja, czy tylko błąd. Czy mógłbyś pokazać mi dokumentację na ten temat? Dziękuję bardzo serdecznie.
Neal.Marlin
29

Musisz ustawić upperView.userInteractionEnabled = NO;, w przeciwnym razie górny widok przechwyci dotknięcia.

Wersja programu Interface Builder to pole wyboru u dołu panelu View Attributes o nazwie „User Interaction Enabled”. Odznacz to i powinieneś być gotowy.

Benjamin Cox
źródło
Przepraszam - powinienem był powiedzieć. Nadal chcę mieć możliwość interakcji z obiektami w górnym UIView.
delany
Ale upperView nie może odebrać żadnego dotknięcia, uwzględnij przycisk w upperView.
imcaptor
2
To rozwiązanie działa dla mnie. Nie potrzebowałem widoku z góry, żeby w ogóle reagować na dotyk.
TJ
11

Niestandardowa implementacja pointInside: withEvent: rzeczywiście wydawała się właściwą drogą, ale obsługa zakodowanych współrzędnych wydawała mi się dziwna. Skończyło się więc na sprawdzeniu, czy CGPoint znajduje się wewnątrz przycisku CGRect za pomocą funkcji CGRectContainsPoint ():

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    return (CGRectContainsPoint(disclosureButton.frame, point));
}
samvermette
źródło
8

Ostatnio napisałem klasę, która pomoże mi właśnie w tym. Używanie go jako klasy niestandardowej dla UIButtonlubUIView spowoduje przekazanie zdarzeń dotykowych, które zostały wykonane na przezroczystym pikselu.

To rozwiązanie jest nieco lepsze niż zaakceptowana odpowiedź, ponieważ nadal możesz kliknąć element UIButtonznajdujący się pod półprzezroczystą, UIViewpodczas gdy nieprzezroczysta część UIViewnadal będzie reagować na zdarzenia dotykowe.

GIF

Jak widać na GIF-ie, przycisk Żyrafa jest prostym prostokątem, ale zdarzenia dotykowe na przezroczystych obszarach są przenoszone na żółty UIButtonpod spodem.

Link do zajęć

Segev
źródło
2
Ponieważ Twój kod nie jest tak długi, powinieneś dołączyć odpowiednie jego fragmenty do swojej odpowiedzi, na wypadek gdyby Twój projekt został przeniesiony lub usunięty w pewnym momencie w przyszłości.
Gavin
Dzięki, Twoje rozwiązanie działa w moim przypadku, w którym nieprzezroczysta część mojego UIView musi reagować na zdarzenia dotykowe, a przezroczysta nie. Znakomity!
Bruce
@Bruce Cieszę się, że ci pomogło!
Segev
4

Chyba trochę się spóźniłem na tę imprezę, ale dodam to możliwe rozwiązanie:

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

Jeśli użyjesz tego kodu do zastąpienia standardowej funkcji hitTest niestandardowego UIView, zignoruje on TYLKO sam widok. Wszelkie podziały tego widoku będą normalnie zwracać trafienia, a wszelkie trafienia, które trafiłyby do samego widoku, są przekazywane do jego superwidoku.

-Popiół

Popiół
źródło
1
to moja preferowana metoda, z wyjątkiem tego, że nie sądzę, abyś wracał [self superview]. dokumentacja dotycząca tej metody zawiera stan „Zwraca najdalszego potomka odbiornika w hierarchii widoków (łącznie ze sobą), który zawiera określony punkt” oraz „Zwraca zero, jeśli punkt leży całkowicie poza hierarchią widoków odbiornika”. myślę, że powinieneś wrócić nil. kiedy zwrócisz zero, kontrola przejdzie do superview, aby sprawdzić, czy ma jakieś trafienia, czy nie. więc zasadniczo zrobi to samo, z wyjątkiem tego, że powrót superwiewu może zepsuć coś w przyszłości.
jasongregori
Jasne, to prawdopodobnie byłoby rozsądne (zwróć uwagę na datę w mojej pierwotnej odpowiedzi - od tamtej pory zrobiłem dużo więcej kodowania)
Ash
4

Po prostu riffowanie na Accepted Answer i umieszczanie tego tutaj w celach informacyjnych. Zaakceptowana odpowiedź działa doskonale. Możesz go rozszerzyć w ten sposób, aby umożliwić podglądom podrzędnym swojego widoku dotarcie lub przekazanie go do wszystkich widoków za nami:

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

Uwaga: nie musisz nawet wykonywać rekursji w drzewie podwidoku, ponieważ każda pointInside:withEvent:metoda zajmie się tym za Ciebie.

Dan Rosenstark
źródło
3

Może pomóc ustawienie wyłączonej właściwości userInteraction. Na przykład:

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

(Uwaga: w powyższym kodzie „self” odnosi się do widoku)

W ten sposób możesz wyświetlać tylko w topView, ale nie będziesz otrzymywać danych wejściowych użytkownika. Wszystkie dotknięcia użytkownika przejdą przez ten widok, a dolny widok odpowie za nie. Użyłbym tego topView do wyświetlania przezroczystych obrazów lub ich animacji.

Aditya
źródło
3

To podejście jest dość przejrzyste i pozwala, aby przezroczyste podglądy nie reagowały również na dotknięcia. Po prostu podklasę UIViewi dodaj następującą metodę do jej implementacji:

@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
prodos
źródło
2

Moje rozwiązanie tutaj:

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

Mam nadzieję że to pomoże

Nocnik
źródło
2

Jest coś, co możesz zrobić, aby przechwycić dotyk w obu widokach.

Widok z góry:

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

Ale taki jest pomysł.

Alexandre Cassagne
źródło
Jest to z pewnością możliwe - ale to dużo pracy (musiałbyś toczyć własne wrażliwe na dotyk obiekty w dolnej warstwie (np. Przyciski), tak myślę?) I wydaje się dziwne, że trzeba by w tym toczyć własne sposób na zachowanie, które wydawałoby się intuicyjne.
delany
Nie jestem pewien, czy może powinniśmy po prostu spróbować.
Alexandre Cassagne
2

Oto wersja Swift:

override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
    return !CGRectContainsPoint(buttonView.frame, point)
}
Esqarrouth
źródło
2

Szybki 3

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    for subview in subviews {
        if subview.frame.contains(point) {
            return true
        }
    }
    return false
}
pyRabbit
źródło
1

Nigdy nie zbudowałem pełnego interfejsu użytkownika przy użyciu zestawu narzędzi UI, więc nie mam z nim dużego doświadczenia. Oto, co moim zdaniem powinno działać.

Każdy UIView, a to UIWindow, ma właściwość subviews, która jest NSArray zawierającą wszystkie podwidoki.

Pierwszy podgląd podrzędny dodany do widoku otrzyma indeks 0, a następny indeks 1 i tak dalej. Można również wymienić addSubview:z insertSubview: atIndex:lub insertSubview:aboveSubview:i takich metod, które może określić pozycję swojej podrzędny w hierarchii.

Sprawdź więc swój kod, aby zobaczyć, który widok dodajesz jako pierwszy do swojego UIWindow. To będzie 0, a druga będzie 1.
Teraz, z jednego z podglądów podrzędnych, aby przejść do drugiego, wykonaj następujące czynności:

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

Daj mi znać, jeśli to zadziała w Twoim przypadku!


(pod tym znacznikiem jest moja poprzednia odpowiedź):

Jeśli widoki muszą się ze sobą komunikować, powinny to robić za pośrednictwem kontrolera (czyli przy użyciu popularnego modelu MVC ).

Tworząc nowy widok, możesz się upewnić, że zarejestruje się on w kontrolerze.

Technika polega więc na tym, aby upewnić się, że widoki są rejestrowane za pomocą kontrolera (który może przechowywać je według nazwy lub cokolwiek wolisz w słowniku lub tablicy). Albo możesz poprosić kontroler o wysłanie wiadomości dla ciebie, albo możesz uzyskać odniesienie do widoku i komunikować się z nim bezpośrednio.

Jeśli twój widok nie ma linku do kontrolera (co może mieć miejsce), możesz użyć singletonów i / lub metod klas, aby uzyskać odwołanie do kontrolera.

nash
źródło
Dziękuję za odpowiedź - ale nie jestem pewien, czy rozumiem. Oba widoki mają kontroler - problem polega na tym, że z jednym widokiem na drugim, dolny widok nie odbiera zdarzeń (i nie przekazuje ich do kontrolera), nawet jeśli nie ma obiektu faktycznie `` blokującego '' te zdarzenia w widok z góry.
delany
Czy masz UIWindow na górze naszych UIViews? Jeśli to zrobisz, wydarzenia powinny być propagowane i nie powinieneś robić żadnej „magii”. Przeczytaj o [Window and Views] [1] w Apple Dev Center (i jeśli to nie pomoże, dodaj kolejny komentarz!) [1]: developer.apple.com/iphone/library/documentation/ iPhone /…
nash
Tak, absolutnie - UIWindow na szczycie hierarchii.
delany
Zaktualizowałem moją odpowiedź, aby zawierała kod do przechodzenia przez hierarchię widoków. Daj mi znać, jeśli będziesz potrzebować innej pomocy!
nash
Dzięki za pomoc nash - ale mam dość duże doświadczenie w budowaniu tych interfejsów, a mój obecny jest skonfigurowany poprawnie, o ile wiem. Wydaje się, że problemem jest domyślne zachowanie pełnych widoków, gdy są one umieszczone nad innymi.
delany
1

Myślę, że właściwym sposobem jest użycie łańcucha widoków wbudowanego w hierarchię widoków. W przypadku widoków podrzędnych, które są wypychane do widoku głównego, nie używaj ogólnego UIView, ale zamiast tego podklasy UIView (lub jednego z jego wariantów, takich jak UIImageView), aby utworzyć MYView: UIView (lub dowolny inny typ nadrzędny, taki jak UIImageView). W implementacji YourView zaimplementuj metodę touchesBegan. Ta metoda zostanie następnie wywołana po dotknięciu tego widoku. Wszystko, co musisz mieć w tej implementacji, to metoda instancji:

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

to dotykaBegan jest interfejsem odpowiadającym, więc nie musisz deklarować go w swoim publicznym ani prywatnym interfejsie; to jeden z tych magicznych interfejsów API, o których musisz tylko wiedzieć. Ten self.superview ostatecznie przesyła żądanie do elementu viewController. W kontrolerze viewController zaimplementuj to dotknięciaBegan do obsługi dotyku.

Zwróć uwagę, że lokalizacja dotknięć (CGPoint) jest automatycznie dostosowywana względem otaczającego widoku, gdy jest przesuwana w górę łańcucha hierarchii widoków.

Tata Smerf
źródło
1

Po prostu chcę to opublikować, bo miałem podobny problem, spędziłem dużo czasu próbując wdrożyć tutaj odpowiedzi bez powodzenia. Co ostatecznie zrobiłem:

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

i wdrażanie UIGestureRecognizerDelegate:

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

Widok z dołu był kontrolerem nawigacyjnym z wieloma segmentami, a na górze miałem coś w rodzaju drzwi, które można było zamknąć gestem przesuwania. Całość została osadzona w kolejnym VC. Działał jak urok. Mam nadzieję że to pomoże.

stringCode
źródło
1

Wdrożenie Swift 4 dla rozwiązania opartego na HitTest

let hitView = super.hitTest(point, with: event)
if hitView == self { return nil }
return hitView
BiscottiGelato
źródło
0

Wywodzący się z doskonałej i przede wszystkim niezawodnej odpowiedzi Stuarta oraz użytecznej implementacji Segeva, oto pakiet Swift 4, który możesz wrzucić do dowolnego projektu:

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

A potem z 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)
}
brandonscript
źródło