Wie UIScrollView abgeleitet wird und die Delegat-Eigenschaft privat gemacht wird

Hier ist, was ich erreichen will:

Möchte ich Sie um eine Unterklasse eines UIScrollView zusätzliche Funktionalität. Diese Unterklasse sollten in der Lage sein, zu reagieren auf das scrollen, so habe ich die delegate-Eigenschaft, um sich selbst zu erhalten, Veranstaltungen wie:

- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView { ... }

Auf der anderen Seite, andere Klassen sollten noch in der Lage sein, diese Ereignisse zu, wie Sie mithilfe der Basis-Klasse UIScrollView.

Also ich hatte verschiedene Ideen, wie dieses problem zu lösen, aber alle diese sind nicht ganz befriedigend, mir 🙁

Meine wichtigste Ansatz ist..über eine eigene Delegaten-Eigenschaft wie folgt:

@interface MySubclass : UIScrollView<UIScrollViewDelegate>
@property (nonatomic, assign) id<UIScrollViewDelegate> myOwnDelegate;
@end

@implementation MySubclass
@synthesize myOwnDelegate;

- (id) initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.delegate = self;
    }
    return self;
}

//Example event
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    //Do something custom here and after that pass the event to myDelegate
    ...
    [self.myOwnDelegate scrollViewDidEndDecelerating:(UIScrollView*)scrollView];
}
@end

So meine Unterklasse kann etwas besonderes machen, wenn das geerbte scrollview endet scrollen, aber immer noch informiert die externe delegieren der Veranstaltung. Das funktioniert so weit. Aber ich will diese Unterklasse zur Verfügung, um anderen Entwicklern, ich soll der Zugriff auf die Basisklasse delegieren Eigentum, wie es sein sollte nur verwendet werden von der Unterklasse. Ich denke, es ist sehr wahrscheinlich, dass andere devs intuitiv verwenden Sie die delegate-Eigenschaft der Basisklasse, auch wenn ich Kommentar-das problem in der header-Datei. Wenn jemand verändert die delegate-Eigenschaft der Unterklasse nicht tun, was es tun soll, und ich kann nicht alles tun, um zu verhindern, dass die rechten jetzt. Und das ist der Punkt, wo ich nicht die geringste Ahnung, wie es zu lösen.

Was ich versucht habe versucht, Sie zu überschreiben, die delegate-Eigenschaft, um es readonly so:

@interface MySubclass : UIScrollView<UIScrollViewDelegate>
...
@property (nonatomic, assign, readonly) id<UIScrollViewDelegate>delegate;
@end

@implementation MySubclass
@property (nonatomic, assign, readwrite) id<UIScrollViewDelegate>delegate;
@end

Führen, in der eine Warnung

"Attribute 'readonly' of property 'delegate' restricts attribute 'readwrite' of property inherited from 'UIScrollView'

Ok schlechte Idee, da bin ich natürlich gegen liskovs substitutionsprinzip hier.

Nächsten probieren --> Versuchen, zu überschreiben, zu delegieren setter wie diese:

...
- (void) setDelegate(id<UIScrollViewDelegate>)newDelegate {
    if (newDelegate != self) self.myOwnDelegate = newDelegate;
    else _delegate = newDelegate; //<--- This does not work!
}
...

Als kommentierte das Beispiel nicht kompilieren, wie es scheint, dass die _delegate ivar wurde nicht gefunden?! So sah ich die header-Datei von UIScrollView und gefunden:

@package
    ...
    id           _delegate;
...

Das @package-Richtlinie schränkt den Zugriff der _delegate ivar, zugänglich nur durch das framework selbst. Also, wenn ich einstellen will _delegate ivar, ich HABE mit der synthetisierten setter. Ich kann nicht sehen, eine Möglichkeit zum überschreiben es in irgendeiner Weise 🙁 Aber ich kann nicht glauben, dass es nicht einen Weg, um dieses, vielleicht kann ich nicht sehen den Wald für die Bäume.

Ich dankbar für jeden Hinweis auf die Lösung des Problems.


Lösung:

Funktioniert es jetzt mit der Lösung von @rob mayoff . Wie ich sagte unten rechts gab es ein problem mit der scrollViewDidScroll: rufen Sie. Ich endlich herausgefunden hat, was das problem ist, auch ich verstehe nicht, warum dies so ist :/

Genau in dem Augenblick, wenn wir uns die super-Delegierten:

- (id) initWithFrame:(CGRect)frame {
    ...
    _myDelegate = [[[MyPrivateDelegate alloc] init] autorelease];
    [super setDelegate:_myDelegate]; <-- Callback is invoked here
}

gibt es einen Rückruf _myDelegate. Der debugger bricht an

- (BOOL) respondsToSelector:(SEL)aSelector {
    return [self.userDelegate respondsToSelector:aSelector];
}

mit der "scrollViewDidScroll:" Selektor als argument.

Das komische Ding, in dieser Zeit selbst.userDelegate nicht noch einstellen und die Punkte auf null, so dass der return-Wert ist NEIN! Das scheint zu verursachen, dass die scrollViewDidScroll: Methoden nicht gefeuert danach. Es sieht aus wie eine Vorprüfung, ob die Methode implementiert, und wenn es nicht gelingt, diese Methode wird nicht gefeuert, auch wenn wir unseren userDelegate Eigenschaft später. Ich weiß nicht, warum dies so ist, wie die meisten anderen Delegierten Methoden nicht über diese Vorprüfung.

Also meine Lösung für dieses ist, zum aufrufen des [super setDelegate...] - Methode in der PrivateDelegate setDelegate Methode, als dies ist der Ort, ich bin mir ziemlich sicher, dass meine userDelegate Methode festgelegt ist.

So, ich werde am Ende mit dieser Umsetzung snippet:

MyScrollViewSubclass.m

- (void) setDelegate:(id<UIScrollViewDelegate>)delegate {
    self.internalDelegate.userDelegate = delegate;  
    super.delegate = self.internalDelegate;
}

- (id) initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.internalDelegate = [[[MyScrollViewPrivateDelegate alloc] init] autorelease];
        //Don't set it here anymore
    }
    return self;
}

Den rest des Codes bleibt unverändert. Ich bin noch nicht wirklich zufrieden mit dieser Problemumgehung, weil Sie es erforderlich macht, rufen Sie die setDelegate-Methode mindestens einmal, aber es funktioniert für meine Bedürfnisse für den moment, obwohl es fühlt sich sehr hacky :/

Wenn jemand noch Ideen hat, wie Sie zu verbessern, ich würde schätzen, dass.

Danke @rob für dein Beispiel!

InformationsquelleAutor der Frage Mexyn | 2012-04-02

Schreibe einen Kommentar