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
Du musst angemeldet sein, um einen Kommentar abzugeben.
Gibt es ein problem mit machen
MySubclass
seinen eigenen Delegierten. Vermutlich wollen Sie nicht, um benutzerdefinierten code auszuführen, für alle derUIScrollViewDelegate
Methoden, aber Sie haben das weiterleiten der Nachrichten an den Benutzer-zur Verfügung gestellt delegieren, ob Sie Ihre eigene Implementierung oder nicht. So könnten Sie versuchen, zu implementieren, die alle von den delegate-Methoden, mit die meisten von Ihnen nur die Weiterleitung wie diese:Das problem hier ist, dass manchmal neue Versionen von iOS hinzufügen neuer delegate-Methoden. Zum Beispiel, iOS 5.0 Hinzugefügt
scrollViewWillEndDragging:withVelocity:targetContentOffset:
. So Ihr scrollview Unterklasse nicht zukunftssicher.Der beste Weg, dies zu behandeln ist, erstellen Sie eine separate, private-Objekt, das nur wirkt als Ihre scrollview - delegate und übernimmt die Weiterleitung. Diese dedicated-delegate-Objekt weiterleiten kann jeder Nachricht, die es erhält, um die vom Benutzer bereitgestellten delegieren, weil es nur erhält delegieren von Nachrichten.
Hier ist, was Sie tun. In Ihrem header-Datei, Sie brauchen nur zu erklären, dass die Schnittstelle für Ihre scrollview-Unterklasse. Sie nicht brauchen, um setzen jede neue Methoden oder Eigenschaften, so dass es nur aussieht wie diese:
MyScrollView.h
Alle wirkliche Arbeit wird in den
.m
- Datei. Zuerst definieren wir die Schnittstelle für die private delegate-Klasse. Ihre Aufgabe ist der Rückruf inMyScrollView
für einige der Delegierten Methoden und weiterleiten alle Nachrichten an die Benutzer delegieren. So wir nur wollen, gebe es Methoden, die ein Teil derUIScrollViewDelegate
. Wir wollen es nicht haben extra Methoden für die Verwaltung eine Referenz auf die Benutzer zu delegieren, so dass wir nur halten, Referenz, wie eine Instanz-variable:MyScrollView.m
Als Nächstes werden wir umsetzen
MyScrollView
. Es braucht eine Instanz zu schaffenMyScrollViewPrivateDelegate
die es braucht, um eigenen. Seit einUIScrollView
nicht die eigenen Delegierten, die wir brauchen, eine zusätzliche, starke Referenz auf dieses Objekt.Müssen wir überschreiben
setDelegate:
unddelegate:
zu speichern, und eine Referenz auf den Benutzer delegieren:Müssen wir auch definieren zusätzliche Methoden, die unsere private delegieren Sie möglicherweise verwenden müssen:
Nun können wir endlich definieren, die Umsetzung der
MyScrollViewPrivateDelegate
. Wir müssen ausdrücklich festlegen, dass jede Methode, die enthalten sollte unsere privaten benutzerdefinierten code. Die Methode muss zur Ausführung unserer benutzerdefinierten code, und leitet die Nachricht an den Benutzer delegieren, wenn der Benutzer, der Delegierte reagiert auf die Nachricht:Und wir müssen mit all den anderen
UIScrollViewDelegate
Methoden, die wir nicht haben benutzerdefinierten code, und alle diese Nachrichten Hinzugefügt werden, die in zukünftigen Versionen von iOS. Wir haben zwei Methoden implementieren, die das möglich machen:InformationsquelleAutor der Antwort rob mayoff
Dank @robmayoff ich gekapselt werden, um eine weitere generische Delegat-interceptor:
Mit original MessageInterceptor Klasse:
MessageInterceptor.h
MessageInterceptor.m
Ist es in Ihrem generischen delegate-Klasse:
GenericDelegate.h
GenericDelegate.m
Damit ich Sie abfangen kann jeder Delegierte muss ich auf jeden UITableView, UICollectionView, UIScrollView... :
In diesem Fall UICollectionViewDelegate scrollViewDidScroll: Funktion wird ausgeführt auf unsere GenericDelegate (mit jedem code, den wir hinzufügen möchten) und auf die Implementierung unserer eigenen Klasse
Thats my 5 Cent, Dank @robmayoff und @jhabbott vorherigen Antwort
InformationsquelleAutor der Antwort M Penades
Eine weitere option ist die Unterklasse und die Kraft der abstrakten Funktionen.
Zum Beispiel, Sie erstellen
Dort überschreiben Sie einige delegate-Methode und definieren Sie Ihre abstrakte Funktion
Alles was Sie jetzt tun müssen, ist, um eine Unterklasse Ihrer EnhancedTableViewController und verwenden Sie Ihre abstrakten Funktionen anstelle von Delegierten ersetzt. Wie diese:
Lassen Sie mich wissen, wenn es etwas falsch hier.
InformationsquelleAutor der Antwort Roman B.
Nicht-Unterklasse, Kapseln es statt 😉
Einen neuen UIView-Unterklasse - Ihre header-Datei würde wie folgt Aussehen :
Und nur eine UIScrollView als Untersicht - Ihre .m-Datei würde wie folgt Aussehen :
Innerhalb der Unterklasse, nur verwenden Sie immer
self.scrollView
und es schaffen das scroll-view das erste mal, wenn Sie danach Fragen.Dies hat den Vorteil, verstecken Sie die scroll-view komplett von jemand mit Ihrem
MySubClass
- wenn Sie benötigt, um zu ändern, wie es hinter den kulissen gearbeitet, (d.h. Wechsel von einem scroll-Ansicht, ein web-Ansicht) es wäre sehr einfach zu tun 🙂Es bedeutet auch, dass niemand Sie ändern können, wie Sie wollen, dass die scroll-view zu Verhalten 🙂
PS ich bin davon ausgegangen BOGEN - ändern
strong
zuretain
und fügen Siedealloc
wenn nötig 🙂BEARBEITEN
Wenn Sie möchten, dass Ihre Klasse sich zu Verhalten genau als ein UIScrollView dann können Sie versuchen, das hinzufügen, dass diese Methode (entnommen aus diese docs und ungetestet!) :
InformationsquelleAutor der Antwort deanWombourne
Für diejenigen, die zu tun diese Art der Sache mit
UIControl
Klassen, siehe meine STAControls Projekt (speziellBase
Klassen wie diese). Ich habe umgesetzt delegieren Weiterleitung für das Projekt mit Rob Mayoff Antwort.InformationsquelleAutor der Antwort Stunner