문제

2페이지로 구성된 UIScrollView가 있고 페이지 사이를 가로로 스크롤할 수 있습니다.그러나 내 페이지 중 하나에 UIDatePicker가 있고 스크롤 보기가 수직 터치 이벤트를 가로채므로 더 이상 날짜 선택기를 조작할 수 없습니다(클릭이나 탭 제외).수직 터치 이벤트를 날짜 선택기로 보내도록 ScrollView에 지시하고, 페이지를 전환하기 위해 수평 터치 이벤트를 스크롤 보기로 보내도록 지시하는 방법이 있습니까?

도움이 되었습니까?

해결책

내 생각에는 이 문제에는 두 부분이 있다.첫 번째는 사용자의 의도를 결정하는 것이고, 두 번째는 해당 의도에 응답하기 위한 올바른 제어를 얻는 것입니다.

의도 결정

사용자의 의도가 무엇인지 명확하게 파악하는 것이 중요하다고 생각합니다.다음 시나리오를 상상해 보세요.사용자는 화면을 터치하기 시작하고 손가락을 왼쪽으로 멀리 움직이지만 약간 위로 움직입니다.사용자는 아마도 보기를 스크롤하려고 했을 것이며 날짜를 전혀 변경할 의도는 없었을 것입니다.보기를 스크롤하고 날짜를 변경하는 것은 좋지 않습니다. 특히 화면 밖으로 이동할 때 더욱 그렇습니다.따라서 사용자가 의도하는 바를 확인하기 위해 다음 알고리즘을 제안합니다.

사용자가 화면을 터치하기 시작하면 시작 위치를 기록합니다.사용자의 손가락이 해당 위치에서 멀어지기 시작하면 컨트롤이 전혀 반응하지 않아야 합니다.터치가 시작 위치에서 특정 임계값 거리를 넘어 이동하면 수평으로 더 이동했는지 수직으로 더 이동했는지 확인합니다.수직으로 움직인다면 사용자가 날짜를 변경하려는 것이므로 수평으로 움직이는 부분은 무시하고 날짜만 변경합니다.더 수평으로 이동했다면 사용자가 뷰를 스크롤하려는 것이므로 수직 이동 부분을 무시하고 뷰만 스크롤합니다.

구현

이를 구현하려면 UIScrollView 또는 날짜 선택기가 수행되기 전에 이벤트를 처리해야 합니다.이를 수행하는 방법에는 여러 가지가 있을 수 있지만 특히 마음에 드는 방법은 다음과 같습니다.ScrollingDateMediatorView라는 사용자 정의 UIView를 만듭니다.UIScrollView를 이 보기의 자식으로 설정합니다.ScrollingDateMediatorView의 hitTest:withEvent를 재정의합니다.그리고 pointInside:withEvent:행동 양식.이러한 메서드는 일반적으로 발생하는 것과 동일한 종류의 적중 테스트를 수행해야 하지만 결과가 날짜 선택기인 경우 대신 self를 반환합니다.이는 날짜 선택기로 예정된 모든 터치 이벤트를 효과적으로 가로채어 ScrollingDateMediatorView가 이를 먼저 처리할 수 있도록 합니다.그런 다음 다양한 touch* 메서드에서 위에 설명된 알고리즘을 구현합니다.구체적으로:

touchesBegan:withEvent 메소드에서 시작 위치를 저장합니다.

touchesMoved:withEvent에서 사용자의 의도를 아직 알 수 없는 경우 터치된 항목이 시작 위치에서 충분히 멀리 이동했는지 확인합니다.그렇다면 사용자가 날짜를 스크롤하거나 변경할 것인지 확인하고 해당 의도를 저장합니다.사용자의 의도가 이미 알려져 있고 날짜를 변경하려는 경우 날짜 선택기에 touchesMoved:withEvent 메시지를 보내고, 그렇지 않으면 UIScrollView에 touchesMoved:withEvent 메시지를 보냅니다.다른 보기가 적절한 메시지를 받는지 확인하려면 touchesEnded:withEvent 및 touchesCancelled:withEvent 내에서 몇 가지 유사한 작업을 수행해야 합니다.두 가지 방법 모두 저장된 값을 재설정해야 합니다.

이벤트를 적절하게 전파한 후에는 이동 임계값을 조정하기 위해 일부 사용자 테스트를 시도해야 할 것입니다.

다른 팁

실제로 Bob이 제안한 것보다 훨씬 간단한 구현이 있습니다. 이것은 나를 위해 완벽하게 작동합니다. 아직 Uiscrollview를 서브 클래스해야하고 아직이 방법을 포함시켜야합니다.

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView* result = [super hitTest:point withEvent:event];

    if ([result.superview isKindOfClass:[UIPickerView class]])
    {
         self.canCancelContentTouches = NO;  
         self.delaysContentTouches = NO;
    }
    else 
    {
         self.canCancelContentTouches = YES; // (or restore bool from prev value if needed)
         self.delaysContentTouches = YES;    // (same as above)
    }
    return result;
}

내가 사용하는 이유 result.superview 터치를 얻는 뷰는 실제로 개인 API 인 uipickertable입니다.

건배

굉장한 도움 샘! 나는 그것을 사용하는 데 사용했는데,이 방법을 스위이즈하는 간단한 범주를 만들기 위해 사용했습니다 (uitableviewcontroller에서 이것을하고 있었기 때문에 스크롤보기를 서브 클래스하기 위해 정말 지저분한 것들을해야했기 때문입니다).

#import <UIKit/UIKit.h>

@interface UIScrollView (withControls)

+ (void) swizzle;

@end

그리고 주요 코드 :

#import </usr/include/objc/objc-class.h>
#import "UIScrollView+withControls.h"

#define kUIViewBackgroundImageTag 6183746
static BOOL swizzled = NO;

@implementation UIScrollView (withControls)

+ (void)swizzleSelector:(SEL)orig ofClass:(Class)c withSelector:(SEL)new;
{
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, new);

    if (class_addMethod(c, orig, method_getImplementation(newMethod),
                        method_getTypeEncoding(newMethod))) {
        class_replaceMethod(c, new, method_getImplementation(origMethod),
                            method_getTypeEncoding(origMethod));
    } else {
        method_exchangeImplementations(origMethod, newMethod);
    }
}

+ (void) swizzle {
    @synchronized(self) {
        if (!swizzled) {

            [UIScrollView swizzleSelector:@selector(hitTest:withEvent:)
                                  ofClass:[UIScrollView class]
                             withSelector:@selector(swizzledHitTest:withEvent:)];
            swizzled = YES;
        }
    }
}

- (UIView*)swizzledHitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView* result = [self swizzledHitTest:point withEvent:event]; // actually calling the original hitTest method

    if ([result.superview isKindOfClass:[UIPickerView class]]) {
        self.canCancelContentTouches = NO;  
        self.delaysContentTouches = NO;
    } else {
        self.canCancelContentTouches = YES; // (or restore bool from prev value if needed)
        self.delaysContentTouches = YES;    // (same as above)
    }
    return result;
}

@end

그런 다음 내 ViewDidload 메소드에서 방금 전화했습니다.

[UIScrollView swizzle];
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top