Frage

Ich habe eine UIScrollView mit 2 Seiten, und ich kann horizontal zwischen ihnen bewegen. Doch auf einem meiner Seiten habe ich eine UIDatePicker und die Scroll-Ansicht wird die vertikalen Touch-Ereignisse abfangen, so kann ich nicht mehr die Datumsauswahl manipulieren (außer durch Klicken oder tippen). Gibt es eine Möglichkeit die Scroll zu sagen, die vertikalen Touch-Ereignisse auf die Datumsauswahl zu senden, aber die horizontalen Touch-Ereignisse auf die Scroll-Ansicht senden Seiten wechseln?

War es hilfreich?

Lösung

Ich denke, es gibt zwei Teile dieses Problem. Der erste ist der Benutzer die Absicht zu bestimmen, und das zweite ist die richtige Kontrolle geraten zu dieser Absicht zu reagieren.

Bestimmung Intent

Ich denke, es ist wichtig, klar zu sein über das, was die Nutzer wollen. Stellen Sie sich folgendes Szenario vor: Der Benutzer startet den Bildschirm berührt und bewegt seinen Finger weit nach links, sondern auch nach oben ein wenig. Der Benutzer wahrscheinlich sollte die Ansicht blättern, und die Absicht hat, überhaupt nicht um das Datum zu ändern. Es wäre schlecht, um sowohl die Ansicht blättern und das Datum ändern, vor allem wie es aus dem Bildschirm bewegt. So, um zu bestimmen, was der Benutzer will ich vorschlagen, den folgenden Algorithmus:

Wenn der Benutzer beginnt, den Bildschirm zu berühren, notieren Sie die Ausgangsposition zurück. Wie der Finger des Benutzers von dieser Position zu bewegen beginnt weg, sollten die Kontrollen überhaupt nicht reagieren. Sobald die Berührung bewegt sich über einen bestimmten Schwellenabstand von der Startposition, festzustellen, ob es horizontal oder vertikal bewegt. Wenn er sich vertikal bewegt, will der Benutzer das Datum ändern, so dass die horizontalen Teil der Bewegung ignorieren und nur das Datum ändern. Wenn es mehr horizontal bewegt, will der Benutzer die Ansicht scrollen, so ignorieren den vertikalen Teil der Bewegung und bewegen nur die Ansicht.

Implementierung

Um dies zu implementieren, müssen Sie die Ereignisse behandeln, bevor die UIScrollView oder Datumsauswahl tun. Wahrscheinlich gibt es ein paar Möglichkeiten, dies zu tun, aber man insbesondere in den Sinn kommt: Erstellen Sie eine benutzerdefinierte UIView genannt ScrollingDateMediatorView. Stellen Sie die UIScrollView als Kind dieser Ansicht. Überschreiben Sie die ScrollingDateMediatorView des hitTest: withevent: und pointInside: withevent: Methoden. Diese Verfahren müssen die gleiche Art von Treffertests durchzuführen, die normalerweise auftreten würde, aber wenn das Ergebnis der Datumsauswahl, kehren selbst statt. Diese hijacks effektiv alle Touch-Ereignisse, die für die Datumsauswahl bestimmt waren, so dass der ScrollingDateMediatorView sie zuerst behandeln. Dann implementieren Sie die oben beschriebene Algorithmus in die verschiedenen Methoden berührt *. Im Einzelnen:

Im touchesBegan. Withevent Methode, um die Startposition speichern

In touchesMoved: Withevent, wenn die Absicht des Benutzers noch nicht bekannt ist, festzustellen, ob die weit genug weg von der Ausgangsposition bewegt berührt hat. Wenn ja, festzustellen, ob der Benutzer zu blättern oder das Datum ändern beabsichtigt, und diese Absicht speichern. Withevent Nachricht, andernfalls senden die die UIScrollView touchesMoved: Wenn die Absicht des Benutzers bereits bekannt ist, und es ist das Datum zu ändern, das touchedMoved der Datumsauswahl senden Withevent Nachricht. Sie werden einige simliar Arbeit innerhalb touchesEnded zu tun haben: Withevent und touchesCancelled: Withevent, um sicherzustellen, die anderen Ansichten, die die entsprechenden Meldungen erhalten. Beide Methoden sollten die gespeicherten Werte zurückgesetzt.

Sobald Sie haben es Ereignisse richtig ausbreiten, werden Sie wahrscheinlich einige Benutzertests zur Abstimmung der Bewegungsschwelle haben, um zu versuchen.

Andere Tipps

Eigentlich ist es eine viel einfachere Implementierung als das, was Bob vorgeschlagen. Das funktioniert perfekt für mich. Sie müssen Ihre UIScrollView Unterklasse, wenn Sie nicht bereits haben, und schließen Sie diese Methode: -

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

Der Grund, warum ich result.superview verwenden ist, dass die Ansicht, die die Berührungen bekommt tatsächlich eine UIPickerTable sein, die eine private API ist.

Prost

Awesome Hilfe Sam! Früher habe ich, dass eine einfache Kategorie zu erstellen, die die Methode Swizzles (weil ich dies in einem UITableViewController tun und damit etwas wirklich chaotisch Sachen zu tun gehabt hätte, die Scroll-Ansicht, Unterklasse).

#import <UIKit/UIKit.h>

@interface UIScrollView (withControls)

+ (void) swizzle;

@end

Und der Hauptcode:

#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

Dann in meiner viewDidLoad Methode, ich habe gerade genannt

[UIScrollView swizzle];
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top