다른 UIView에서 UIView와의 상호 작용 허용
다른 UIView 아래에있는 UIView의 버튼과 상호 작용할 수있는 간단한 방법이 있습니까? 버튼 맨 위에있는 UIView의 실제 객체가없는 곳입니까?
예를 들어, 현재 상단에 객체가 있고 화면 하단에 객체가 있고 가운데에는 아무것도없는 UIView (A)가 있습니다. 이것은 중간에 버튼이있는 다른 UIView 위에 있습니다 (B). 그러나 B 중간에있는 버튼과 상호 작용할 수없는 것 같습니다.
B의 버튼을 볼 수 있습니다-A의 배경을 clearColor로 설정했지만 B의 버튼은 실제로 A의 객체가 해당 버튼 위에 없다는 사실에도 불구하고 터치를 수신하지 않는 것 같습니다.
편집 -여전히 최상위 UIView에서 객체와 상호 작용할 수 있기를 원합니다.
분명히 이것을하는 간단한 방법이 있습니까?
상위 뷰에 대한 UIView 서브 클래스를 작성하고 다음 메소드를 대체해야합니다.
- (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);
}
hitTest : event : 메소드를 볼 수도 있습니다.
여기에 많은 답변이 효과가 있지만 가장 편리하고 포괄적이며 완벽한 답변이 여기에 없다는 것을 알게되어 약간 놀랐습니다. @Ash는 슈퍼 뷰를 반환하는 데 이상한 일이 있다는 것을 제외하고는 가장 가까워졌습니다 ...하지 마십시오.
이 답변은 여기 비슷한 질문에 대한 답변에서 가져 왔습니다 .
- (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]
터치 한 뷰의 계층 구조에서 가장 깊은 뷰를 반환합니다. 만약 hitView == self
(즉, 터치 지점 아래에는 서브 뷰가없는 경우), 반환 nil
이 뷰 것을 지정 하지 터치를받을 수 있습니다. 응답자 체인이 작동하는 방식은이 지점 위의 뷰 계층 구조가 터치에 반응하는 뷰가 발견 될 때까지 계속 이동 함을 의미합니다. 수퍼 뷰가 터치를 허용해야하는지 여부에 대해서는 수퍼 뷰를 반환 하지 마십시오 !
이 솔루션은 다음과 같습니다
- 다른 뷰 / 서브 뷰 / 객체에 대한 참조가 필요하지 않기 때문에 편리 합니다.
- generic 은 터치 가능한 서브 뷰의 컨테이너 역할을하는 뷰에 적용되기 때문에 서브 뷰의 구성은 작동 방식에 영향을 미치지 않습니다 (
pointInside:withEvent:
특정 터치 가능한 영역을 반환하도록 재정의 하는 경우와 동일 ). - 바보 , 코드가 많지 않습니다 ... 그리고 개념은 머리를 돌리기 어렵지 않습니다.
나는 이것을 하나의 오버라이드를 위해 무의미한 뷰 서브 클래스를 저장하기 위해 서브 클래스로 추상화하기에 충분히 자주 사용합니다. 보너스로 구성 가능한 속성을 추가하십시오.
@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
그런 다음 평범한 곳을 어디에서나 사용할 수 있습니다 UIView
. 로 설정하는 것은 간단 onlyRespondToTouchesInSubviews
합니다 YES
.
이를 처리 할 수있는 몇 가지 방법이 있습니다. 내가 가장 좋아하는 것은 충돌하는 뷰 (이러한 A 및 B라고하는 소리)에 대한 공통 슈퍼 뷰 (간접적으로) 인 뷰에서 hitTest : withEvent :를 재정의하는 것입니다. 예를 들어 다음과 같습니다 (여기서 A와 B는 UIView 포인터입니다. 여기서 B는 "숨겨진"포인터이며 일반적으로 무시됩니다).
- (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];
}
pointInside:withEvent:
gyim이 제안한대로 방법을 수정할 수도 있습니다 . 이를 통해 최소한 터치에 대해 A에서 "구멍을 파지"함으로써 본질적으로 동일한 결과를 얻을 수 있습니다.
또 다른 방법은 이벤트 전달입니다.이 방법은 우선 적용되는 위치 touchesBegan:withEvent:
와 touchesMoved:withEvent:
다른 객체에 일부 터치를 전송 하는 재정의 및 유사한 방법 (예 : 등)을 의미 합니다. 예를 들어 A에서 다음과 같이 작성할 수 있습니다.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([self shouldForwardTouches:touches]) {
[B touchesBegan:touches withEvent:event];
}
else {
// Do whatever A does with touches.
}
}
그러나 이것이 항상 예상대로 작동 하는 것은 아닙니다 ! 가장 중요한 것은 UIButton과 같은 내장 컨트롤은 항상 전달 된 터치를 무시한다는 것입니다. 이 때문에 첫 번째 접근 방식이 더 안정적입니다.
아이디어를 시연하는 작은 xcode 프로젝트와 함께이 모든 것을 자세히 설명하는 좋은 블로그 게시물이 있습니다.
http://bynomial.com/blog/?p=74
을 설정해야합니다 upperView.userInteractionEnabled = NO;
. 그렇지 않으면 상단보기에서 터치가 차단됩니다.
이 인터페이스 빌더 버전은 속성보기 패널의 맨 아래에 "사용자 상호 작용 사용"이라는 체크 박스입니다. 선택을 해제하면 잘 가야합니다.
pointInside : withEvent :의 사용자 정의 구현은 실제로 갈 길처럼 보였지만 하드 코딩 된 좌표를 처리하는 것은 나에게 이상한 것처럼 보였습니다. 그래서 CGPoint가 CGRectContainsPoint () 함수를 사용하여 CGRect 버튼 안에 있는지 확인했습니다.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return (CGRectContainsPoint(disclosureButton.frame, point));
}
최근에 나는 그것을 도와 줄 수업을 썼습니다. UIButton
또는 사용자 정의 클래스로 사용 하거나 UIView
투명 픽셀에서 실행 된 터치 이벤트를 전달합니다.
이 솔루션은 UIButton
반투명 아래에있는 버튼을 클릭 할 수 있지만 투명 UIView
하지 않은 부분은 UIView
터치 이벤트에 계속 반응 하므로 허용 된 답변보다 다소 낫습니다 .
GIF에서 볼 수 있듯이 기린 버튼은 단순한 사각형이지만 투명한 영역의 터치 이벤트는 UIButton
아래 의 노란색으로 전달됩니다 .
나는이 파티에 약간 늦었다 고 생각하지만 가능한 해결책을 추가 할 것입니다.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView != self) return hitView;
return [self superview];
}
이 코드를 사용하여 사용자 정의 UIView의 표준 hitTest 함수를 대체하면보기 자체 만 무시합니다. 해당 뷰의 모든 서브 뷰는 정상적으로 히트를 반환하고 뷰 자체로 이동 한 히트는 수퍼 뷰로 전달됩니다.
-금연 건강 증진 협회
수락 된 답변을 riffing하고 여기에 참고 용으로 넣으십시오. 수락 된 답변은 완벽하게 작동합니다. 뷰의 하위 뷰가 터치를 받거나 우리 뒤에있는 모든 뷰에 전달할 수 있도록 다음과 같이 확장 할 수 있습니다.
- (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;
}
참고 : 서브 뷰 트리에서 재귀를 수행 할 필요조차 없습니다. 각 pointInside:withEvent:
메소드가이를 처리 하기 때문 입니다.
userInteraction 속성을 비활성화하면 도움이 될 수 있습니다. 예 :
UIView * topView = [[TOPView alloc] initWithFrame:[self bounds]];
[self addSubview:topView];
[topView setUserInteractionEnabled:NO];
(참고 : 위 코드에서 'self'는보기를 나타냅니다)
이 방법으로 topView에만 표시 할 수 있지만 사용자 입력은받지 않습니다. 이러한 모든 사용자 터치는이보기를 통해 수행되고 아래쪽보기는 이에 대해 응답합니다. 이 topView를 사용하여 투명한 이미지를 표시하거나 애니메이션으로 만듭니다.
이 접근 방식은 매우 깨끗하며 투명한 하위 뷰 가 터치에 반응하지 않도록합니다. 서브 클래스 UIView
를 만들고 구현에 다음 메소드를 추가하십시오.
@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
내 해결책은 다음과 같습니다.
-(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];
}
도움이 되었기를 바랍니다
두보기 모두에서 터치를 가로 채기 위해 할 수있는 일이 있습니다.
평면도:
-(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.
}
그러나 그것은 아이디어입니다.
다음은 스위프트 버전입니다.
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
return !CGRectContainsPoint(buttonView.frame, point)
}
스위프트 3
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for subview in subviews {
if subview.frame.contains(point) {
return true
}
}
return false
}
UI 툴킷을 사용하여 완전한 사용자 인터페이스를 구축 한 적이 없으므로 경험이 많지 않습니다. 그래도 작동해야한다고 생각합니다.
Every UIView, and this the UIWindow, has a property subviews
, which is an NSArray containing all the subviews.
The first subview you add to a view will receive index 0, and the next index 1 and so forth. You can also replace addSubview:
with insertSubview: atIndex:
or insertSubview:aboveSubview:
and such methods that can determine the position of your subview in the hierarchy.
So check your code to see which view you add first to your UIWindow. That will be 0, the other will be 1.
Now, from one of your subviews, to reach another you would do the following:
UIView * theOtherView = [[[self superview] subviews] objectAtIndex: 0];
// or using the properties syntax
UIView * theOtherView = [self.superview.subviews objectAtIndex:0];
Let me know if that works for your case!
(below this marker is my previous answer):
If views need to communicate with each other, they should do so via a controller (that is, using the popular MVC model).
When you create a new view, you can make sure it registers itself with a controller.
So the technique is to make sure your views register with a controller (which can store them by name or whatever you prefer in a Dictionary or Array). Either you can have the controller send a message for you, or you can get a reference to the view and communicate with it directly.
If your view doesn't have a link back the controller (which may be the case) then you can make use of singletons and/or class methods to get a reference to your controller.
I think the right way is to use the view chain built into the view hierarchy. For your subviews that are pushed onto the main view, do not use the generic UIView, but instead subclass UIView (or one of its variants like UIImageView) to make MYView : UIView (or whatever supertype you want, such as UIImageView). In the implementation for YourView, implement the touchesBegan method. This method will then get invoked when that view is touched. All you need to have in that implementation is an instance method:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event ;
{ // cannot handle this event. pass off to super
[self.superview touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event]; }
this touchesBegan is a responder api, so you dont need to declare it in your public or private interface; it's one of those magic api's you just have to know about. This self.superview will bubble up the request eventually to the viewController. In the viewController, then, implement this touchesBegan to handle the touch.
Note that the touches location (CGPoint) is automatically adjusted relative to the encompassing view for you as it is bounced up the view hierarchy chain.
Just want to post this, coz I had somewhat similar problem, spent substantial amount of time trying to implement answers here without any luck. What I ended up doing:
for(UIGestureRecognizer *recognizer in topView.gestureRecognizers)
{
recognizer.delegate=self;
[bottomView addGestureRecognizer:recognizer];
}
topView.abView.userInteractionEnabled=NO;
and implementing UIGestureRecognizerDelegate
:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Bottom view was a navigation controller with number of segues and I had sort of a door on top of it that could close with pan gesture. Whole thing was embedded in yet another VC. Worked like a charm. Hope this helps.
Swift 4 Implementation for HitTest based solution
let hitView = super.hitTest(point, with: event)
if hitView == self { return nil }
return hitView
Derived from Stuart's excellent, and mostly foolproof answer, and Segev's useful implementation, here is a Swift 4 package that you can drop into any project:
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
}
}
And then with 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)
}
참고 URL : https://stackoverflow.com/questions/1694529/allowing-interaction-with-a-uiview-under-another-uiview
'Programing' 카테고리의 다른 글
int를 사용하여 python dataframe pandas drop column (0) | 2020.07.21 |
---|---|
MVC에서 PDF를 브라우저로 반환하는 방법은 무엇입니까? (0) | 2020.07.21 |
start-stop-daemon으로 시작한 프로세스의 stdout을 어떻게 기록 할 수 있습니까? (0) | 2020.07.21 |
반응, ES6-getInitialState가 일반 JavaScript 클래스에 정의되었습니다. (0) | 2020.07.21 |
간단한 텍스트 파일 읽기 (0) | 2020.07.21 |