다른 많은보기 위에 오버레이 된보기가 있습니다. 나는 화면에서 몇 가지 터치를 감지하기 위해 Overaly를 사용하고 있지만 그 외에는 뷰가 스크롤 뷰와 같은 다른 뷰의 동작을 멈추고 싶지 않습니다. 모든 터치를 전달하는 방법 이 오버레이 뷰? UIView의 일부입니다.
다른 많은보기 위에 오버레이 된보기가 있습니다. 나는 화면에서 몇 가지 터치를 감지하기 위해 Overaly를 사용하고 있지만 그 외에는 뷰가 스크롤 뷰와 같은 다른 뷰의 동작을 멈추고 싶지 않습니다. 모든 터치를 전달하는 방법 이 오버레이 뷰? UIView의 일부입니다.
답변:
사용자 상호 작용을 비활성화하는 것만으로도 충분했습니다!
목표 -C :
myWebView.userInteractionEnabled = NO;
빠른:
myWebView.isUserInteractionEnabled = false
myWebView.isUserInteractionEnabled = false
오버레이보기에서 아래보기로 터치를 전달하려면 UIView에서 다음 메소드를 구현하십시오.
목표 -C :
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"Passing all touches to the next view (if any), in the view stack.");
return NO;
}
스위프트 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
}
이것은 오래된 스레드이지만 검색시 나왔으므로 2c를 추가 할 것이라고 생각했습니다. 하위보기가있는 덮는 UIView가 있고 하위보기 중 하나에 닿는 터치 만 가로 채기를 원하므로 PixelCloudSt의 답변을 다음과 같이 수정했습니다.
-(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;
}
@fresidue 답변의 개선 된 버전. 이 UIView
서브 클래스는 서브 뷰 외부로 터치를 전달하는 투명한 뷰로 사용할 수 있습니다 . 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
.
그리고 스위프트에서 :
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)
})
}
}
그렇다면 테이블 뷰가있는 큰 "홀더"패널이 있다고 가정 해보십시오. "홀더"패널을 PassthroughView
만듭니다. 이제 작동합니다. "홀더"를 통해 "테이블"을 스크롤 할 수 있습니다.
그러나!
"보유자"패널 위에는 레이블이나 아이콘이 있습니다. 물론 사용자 상호 작용이 활성화 된 것으로 표시되어야합니다.
"홀더"패널 위에는 몇 개의 버튼이 있습니다. 물론 사용자 상호 작용이 활성화 된 것으로 표시되어야합니다.
참고 사용하는보기 - 다소 혼동은 "홀더"자체 있음 PassthroughView
에이 - 표시해야 사용자 상호 작용이 활성화 ON을 ! 그건 ON ! 그렇지 않으면 PassthroughView의 코드는 단순히 호출되지 않습니다.
$0.isUserInteractionEnabled
UIStackView와 비슷한 문제가 있었지만 다른보기 일 수 있습니다. 내 구성은 다음과 같습니다.
측면에 버튼이있는 배경에 배치해야 할 컨테이너가있는 고전적인 경우입니다. 레이아웃 목적으로 UIStackView에 버튼을 포함 시켰지만 이제 stackView의 중간 (빈) 부분이 터치를 차단합니다.
내가 한 것은 터치 할 수있는 subView를 정의하는 속성으로 UIStackView의 서브 클래스를 만드는 것입니다. 이제 사이드 버튼 (* viewsWithActiveTouch * 배열에 포함)을 터치하면 버튼이 표시되고,이 뷰 이외의 다른 곳에서 스택 뷰를 터치하면 가로 채지 않으므로 스택 아래에있는 모든 것에 전달됩니다. 전망.
/** 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
}
}
터치를 전달하려는 뷰가 서브 뷰 / 슈퍼 뷰가 아닌 경우 UIView 서브 클래스에서 다음과 같이 사용자 정의 속성을 설정할 수 있습니다.
@interface SomeViewSubclass : UIView {
id forwardableTouchee;
}
@property (retain) id forwardableTouchee;
당신의 .m에서 그것을 합성하십시오 :
@synthesize forwardableTouchee;
그런 다음과 같은 UIResponder 메소드에 다음을 포함하십시오.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.forwardableTouchee touchesBegan:touches withEvent:event];
}
UIView를 인스턴스화 할 때마다 forwardableTouchee 속성을 이벤트를 전달할보기로 설정하십시오.
SomeViewSubclass *view = [[[SomeViewSubclass alloc] initWithFrame:someRect] autorelease];
view.forwardableTouchee = someOtherView;
스위프트 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
}
}
UIStackView를 통해 터치를 전달해야했습니다. 내부의 UIView는 투명했지만 UIStackView는 모든 터치를 소비했습니다. 이것은 나를 위해 일했다 :
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
}
}
정렬 된 모든 서브 뷰는 여전히 터치를 받지만 UIStackView 자체를 터치하면 아래보기로 이동했습니다 (나에게 mapView).
여기에 많은 답변이있는 것처럼 보이지만, 내가 필요로하는 사람은 신속하지 않습니다. 그래서 나는 여기에서 @fresidue의 대답을 가져 와서 현재 대부분의 개발자가 여기에서 사용하고 싶어하기 때문에 신속하게 변환했습니다.
버튼이있는 투명한 툴바가있는 문제를 해결했지만 툴바가 사용자에게 보이지 않게하고 터치 이벤트가 진행되어야합니다.
isUserInteractionEnabled = 일부는 내 테스트를 기반으로 한 옵션이 아니기 때문에 false입니다.
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
}
StackView 안에 몇 가지 레이블이 있었고 위의 솔루션으로는 큰 성공을 거두지 못했습니다. 대신 아래 코드를 사용하여 문제를 해결했습니다.
let item = ResponsiveLabel()
// Configure label
stackView.addArrangedSubview(item)
서브 클래 싱 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
}
}
그런 다음 다음과 같은 작업을 수행 할 수 있습니다.
class ResponsiveLabel:UILabel{
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Respond to touch
}
}
이런 식으로 시도하십시오 ...
for (UIView *view in subviews)
[view touchesBegan:touches withEvent:event];
예를 들어 touchesBegan 메서드에서 위의 코드는 터치를 모든 하위 뷰로 전달합니다.
내가 하려던 상황은 중첩 된 UIStackView 내부의 컨트롤을 사용하여 제어판을 작성하는 것이 었습니다. 일부 컨트롤에는 UIButton이있는 UITextField가 있습니다. 또한 컨트롤을 식별하는 레이블이있었습니다. 내가하고 싶은 것은 제어판 뒤에 큰 "보이지 않는"버튼을 두어 사용자가 버튼이나 텍스트 필드 외부 영역을 두드리면 잡을 수 있고 조치를 취할 수 있도록했습니다. 텍스트가있는 경우 주로 키보드를 닫습니다. 필드가 활성화되었습니다 (resignFirstResponder). 그러나 제어판의 레이블 또는 다른 빈 영역을 두드리면 통과하지 않습니다. 위의 토론은 아래의 답변을 얻는 데 도움이되었습니다.
기본적으로 UIStackView를 서브 클래 싱하고 "point (inside : with) 루틴을 덮어 써서 터치가 필요한 컨트롤 유형을 찾고 무시하려는 레이블과 같은 것을"무시 "합니다. 또한 UIStackView 내부를 확인하여 제어판 구조로 재귀 할 수 있습니다.
코드는 아마도 좀 더 장황하다. 그러나 디버깅에 도움이되었으므로 루틴이 수행하는 작업에 더 많은 명확성을 제공하기를 바랍니다. Interface Builder에서 UIStackView의 클래스를 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
}
}
@PixelCloudStv가 제안한 것처럼 한 뷰에서 다른 뷰로 터치하지만이 프로세스를 추가로 제어하려면 하위 클래스 UIView
//헤더
@interface TouchView : UIView
@property (assign, nonatomic) CGRect activeRect;
@end
//이행
#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
interfaceBuilder에서 View 클래스를 TouchView로 설정하고 rect로 활성 rect를 설정하십시오. 또한 다른 논리를 변경하고 구현할 수 있습니다.