سؤال

لدي UISCrollView مع صفحتين، ويمكنني التمرير أفقيا بينهما. ومع ذلك، في إحدى صفحاتي، لدي UIDEVERPICKER، ويعترض عرض التمرير أحداث اللمس الرأسي حتى لا أستطيع التعامل مع منتقي التاريخ (باستثناء النقر أو النقر). هل هناك طريقة لإخبار ScrollView لإرسال أحداث اللمس العمودية إلى منتقي التاريخ، ولكن أرسل أحداث اللمس الأفقي إلى عرض التمرير للتبديل الصفحات؟

هل كانت مفيدة؟

المحلول

أعتقد أن هناك جزأين لهذه المشكلة. الأول هو تحديد نية المستخدم، والثاني هو الحصول على السيطرة الصحيحة للاستجابة لهذه النية.

تحديد النوايا

أعتقد أنه من المهم أن تكون واضحا بشأن ما يعتزمه المستخدم. تخيل هذا السيناريو: يبدأ المستخدم لمس الشاشة وينقل إصبعه بعيدا إلى اليسار، ولكن أيضا ما يصل قليلا. ربما يهدف المستخدم إلى تمرير العرض، ولم ينوي تغيير التاريخ على الإطلاق. سيكون الأمر سيئا لكلا التمرير في طريقة العرض وتغيير التاريخ، خاصة كما يتحرك خارج الشاشة. لذلك لتحديد ما يعتزم المستخدم أقترح الخوارزمية التالية:

عندما يبدأ المستخدم لمس الشاشة، قم بتسجيل موضع البداية. مع بدء إصبع المستخدم في الابتعاد عن هذا الموضع، يجب ألا تتفاعل عناصر التحكم على الإطلاق. بمجرد أن تتحرك Touch بعد مسافة عتبة معينة من موضع البداية، حدد ما إذا كانت نقلت أكثر أفقيا أو رأسيا. إذا انتقلت رأسيا، فإن المستخدم يعتزم تغيير التاريخ، لذلك تجاهل الجزء الأفقي من الحركة وتغيير التاريخ فقط. إذا انتقلت أكثر أفقيا، فإن المستخدم يعتزم التمرير العرض، لذلك تجاهل الجزء الرأسي للحركة وانتقل فقط عرض العرض.

تطبيق

من أجل تنفيذ هذا، تحتاج إلى التعامل مع الأحداث قبل القيام UISCrollView أو Picker Date. ربما هناك عدة طرق للقيام بذلك، ولكن يتباطأ أحد على وجه الخصوص إلى الذهن: قم بإجراء Uiview مخصص يسمى ScrollingDateMediatorView. قم بتعيين uiscrollView كطفل من هذا الرأي. قم بتجاوز الحد الأدنى للتمرير SCROLLINGDATITITOITOITSOVENT: WITEVENT: و PointinSide: withent: الأساليب. تحتاج هذه الطرق إلى أداء نفس النوع من الاختبار الضرب الذي يحدث عادة، ولكن إذا كانت النتيجة هي منتقي التاريخ، فارجع الذات بدلا من ذلك. هذه الاختطاف بشكل فعال أي أحداث تعمل باللمس تم توجيهها إلى منتقي التاريخ، مما يتيح ل ScrollingDateMediatorView لمعالجةها أولا. ثم تقوم بتنفيذ الخوارزمية الموصوفة أعلاه في طرق اللمسات المختلفة *. خاصة:

في اللمس: طريقة Withevent، احفظ موضع البداية.

في اللمس: أديفنت، إذا كان نية المستخدم غير معروف حتى الآن، فحدد ما إذا كانت المس المستحقة قد انتقلت بعيدا بما فيه الكفاية بعيدا عن موضع البداية. إذا كان لديه، حدد ما إذا كان المستخدم يعتزم التمرير أو تغيير التاريخ، وحفظ تلك النية. إذا كان نية المستخدم معروفا بالفعل، فهو تغيير التاريخ، وإرسال المتقاطعة التاريخ The TouchEdmoved: رسالة Withevent، وإلا ترسل UISCrollView لوحة اللمس: رسالة. سيتعين عليك القيام ببعض الأعمال الرمزية داخل اللمس: Withevent و touchscancelled: أبلغت للتأكد من أن الآراء الأخرى تحصل على الرسائل المناسبة. يجب إعادة تعيين كل من هذه الأساليب القيم المحفوظة.

بمجرد أن يكون لديك أحداث تنتشر بشكل صحيح، ربما عليك تجربة بعض اختبارات المستخدم لحن عتبة الحركة.

نصائح أخرى

في الواقع، هناك تطبيق أبسط بكثير مما اقترح به بوب. هذا يعمل تماما بالنسبة لي. ستحتاج إلى تصنيف 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 هو أن الرأي الذي يحصل على اللمسات سيكون في الواقع Uipickerable، وهو API الخاص.

هتافات

مساعدة رهيبة سام! استخدمت ذلك لإنشاء فئة بسيطة تتسلق الطريقة (نظرا لأنني أقوم بذلك في VietViewController وبالتالي كان عليك القيام ببعض الاشياء الفوضى حقا لتقييم عرض التمرير).

#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

ثم، في طريقة التحميل الخاصة بي، اتصلت فقط

[UIScrollView swizzle];
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top