Ich habe eine UITableView mit einer Liste von Elementen. Durch Auswahl eines Elements wird ein viewController verschoben, der dann die folgenden Schritte ausführt. Von der Methode viewDidLoad Ich starte eine URLRequest für Daten, die von einer meiner Unteransichten benötigt werden - eine UIView-Unterklasse mit überschriebenem drawRect. Wenn die Daten aus der Cloud ankommen, beginne ich mit dem Aufbau meiner Ansichtshierarchie. Die betreffende Unterklasse erhält die Daten und die drawRect-Methode verfügt nun über alles, was zum Rendern erforderlich ist.
Aber.
Da ich drawRect nicht explizit aufrufe - Cocoa-Touch übernimmt das -, kann ich Cocoa-Touch nicht darüber informieren, dass ich wirklich, wirklich möchte, dass diese UIView-Unterklasse gerendert wird. Wann? Jetzt wäre gut!
Ich habe [myView setNeedsDisplay] ausprobiert. Das funktioniert manchmal irgendwie. Sehr fleckig.
Ich habe stundenlang damit gerungen. Könnte jemand, der mir bitte einen soliden, garantierten Ansatz bietet, um ein UIView-Re-Rendering zu erzwingen.
Hier ist der Codeausschnitt, der der Ansicht Daten zuführt:
// Create the subview
self.chromosomeBlockView = [[[ChromosomeBlockView alloc] initWithFrame:frame] autorelease];
// Set some properties
self.chromosomeBlockView.sequenceString = self.sequenceString;
self.chromosomeBlockView.nucleotideBases = self.nucleotideLettersDictionary;
// Insert the view in the view hierarchy
[self.containerView addSubview:self.chromosomeBlockView];
[self.containerView bringSubviewToFront:self.chromosomeBlockView];
// A vain attempt to convince Cocoa-Touch that this view is worthy of being displayed ;-)
[self.chromosomeBlockView setNeedsDisplay];
Prost, Doug
quelle
setNeedsDisplay
und dem tatsächlichen Anruf von auftrittdrawRect:
. Während die Anrufzuverlässigkeit hier ist, würde ich dies nicht als die „robusteste“ Lösung bezeichnen - eine robuste Zeichnungslösung sollte theoretisch die gesamte Zeichnung unmittelbar vor der Rückkehr zum Client-Code ausführen, der die Zeichnung erfordert.Ich hatte ein Problem mit einer großen Verzögerung zwischen dem Aufruf von setNeedsDisplay und drawRect: (5 Sekunden). Es stellte sich heraus, dass ich setNeedsDisplay in einem anderen Thread als dem Hauptthread aufgerufen habe. Nachdem dieser Aufruf in den Haupt-Thread verschoben wurde, verschwand die Verzögerung.
Hoffe das ist eine Hilfe.
quelle
Die Geld-zurück-garantierte, Stahlbeton-solide Methode, um eine Ansicht zum synchronen Zeichnen zu zwingen (bevor Sie zum aufrufenden Code zurückkehren), besteht darin, die
CALayer
Interaktionen desUIView
Benutzers mit Ihrer Unterklasse zu konfigurieren .Erstellen Sie in Ihrer UIView-Unterklasse eine
displayNow()
Methode, die der Ebene sagt, dass sie " Kurs für die Anzeige festlegen " und dann "so machen " soll:Schnell
Ziel c
Implementieren Sie auch eine
draw(_: CALayer, in: CGContext)
Methode, die Ihre private / interne Zeichenmethode aufruft (was funktioniert, da jede aUIView
istCALayerDelegate
) :Schnell
Ziel c
Und erstellen Sie Ihre benutzerdefinierte
internalDraw(_: CGRect)
Methode zusammen mit ausfallsicherdraw(_: CGRect)
:Schnell
Ziel c
Und jetzt rufen
myView.displayNow()
Sie einfach an, wann immer Sie es wirklich brauchen, um zu zeichnen (z. B. von einemCADisplayLink
Rückruf) . UnseredisplayNow()
Methode teilt dasCALayer
to mitdisplayIfNeeded()
, das synchron in unser zurückruftdraw(_:,in:)
und das ZeichneninternalDraw(_:)
vornimmt, und aktualisiert das Bild mit dem, was in den Kontext gezeichnet wird, bevor Sie fortfahren.Dieser Ansatz ähnelt dem oben beschriebenen von @ RobNapier, hat jedoch den Vorteil, dass
displayIfNeeded()
zusätzlich aufgerufen wirdsetNeedsDisplay()
, wodurch er synchron ist.Dies ist möglich, weil
CALayer
s mehr ZeichenfunktionenUIView
bieten als s - Ebenen sind niedriger als Ansichten und wurden explizit für den Zweck einer hoch konfigurierbaren Zeichnung innerhalb des Layouts entworfen und (wie viele Dinge in Cocoa) so konzipiert, dass sie flexibel verwendet werden können ( als Elternklasse oder als Delegator oder als Brücke zu anderen Zeichensystemen oder nur für sich allein). Die ordnungsgemäße Verwendung desCALayerDelegate
Protokolls macht dies alles möglich.Weitere Informationen zur Konfigurierbarkeit von
CALayer
s finden Sie im Abschnitt Einrichten von Ebenenobjekten im Core Animation Programming Guide .quelle
drawRect:
explizit angegeben ist: "Sie sollten diese Methode niemals direkt selbst aufrufen." Außerdemdisplay
sagt CALayer ausdrücklich: "Rufen Sie diese Methode nicht direkt auf." Wenn Siecontents
direkt auf den Ebenen synchron zeichnen möchten, müssen Sie diese Regeln nicht verletzen. Sie könnencontents
jederzeit auf die Ebenen zeichnen (auch auf Hintergrund-Threads). Fügen Sie dazu einfach eine Unterebene zur Ansicht hinzu. Dies unterscheidet sich jedoch von der Anzeige auf dem Bildschirm, bei der auf die richtige Compositing-Zeit gewartet werden muss.contents
("Wenn das Ebenenobjekt an ein Ansichtsobjekt gebunden ist, sollten Sie vermeiden, den Inhalt dieser Eigenschaft direkt festzulegen . Das Zusammenspiel zwischen Ansichten und Ebenen ergibt sich normalerweise." in der Ansicht, den Inhalt dieser Eigenschaft während eines nachfolgenden Updates zu ersetzen. ") Ich empfehle diesen Ansatz nicht besonders; Vorzeitiges Zeichnen beeinträchtigt die Leistung und die Zeichenqualität. Aber wenn Sie es aus irgendeinem Grund brauchen, danncontents
ist, wie Sie es bekommen.drawRect:
direktem Anruf genommen . Es war nicht notwendig, diese Technik zu demonstrieren, und wurde behoben.contents
Technik betrifft, die Sie vorschlagen ... klingt verlockend. Ich habe anfangs so etwas versucht, konnte es aber nicht zum Laufen bringen und fand, dass die obige Lösung viel weniger Code ist, ohne Grund, nicht genauso performant zu sein. Wenn Sie jedoch eine funktionierende Lösung für diesencontents
Ansatz haben, würde ich sie gerne lesen (es gibt keinen Grund, warum Sie nicht zwei Antworten auf diese Frage haben können, oder?)display
. Es ist nur eine benutzerdefinierte öffentliche Methode in einer UIView-Unterklasse, genau wie in GLKView (eine andere UIView-Unterklasse und die einzige von Apple geschriebene, von der ich weiß, dass sie DRAW NOW! -Funktionalität erfordert ).Ich hatte das gleiche Problem und alle Lösungen von SO oder Google funktionierten nicht für mich. Normalerweise
setNeedsDisplay
funktioniert es, aber wenn es nicht funktioniert ...Ich habe versucht,
setNeedsDisplay
die Ansicht auf jede mögliche Weise von allen möglichen Threads und Dingen aufzurufen - immer noch kein Erfolg. Wir wissen, wie Rob sagte, dassAber aus irgendeinem Grund würde es diesmal nicht zeichnen. Und die einzige Lösung, die ich gefunden habe, besteht darin, sie nach einiger Zeit manuell aufzurufen, damit alles, was die Auslosung blockiert, wie folgt vergeht:
Dies ist eine gute Lösung, wenn Sie die Ansicht nicht häufig zum Neuzeichnen benötigen. Andernfalls gibt es normalerweise keine Probleme, wenn Sie nur telefonieren (Action)
setNeedsDisplay
.Ich hoffe, es wird jemandem helfen, der dort verloren ist, so wie ich es war.
quelle
Nun, ich weiß, dass dies eine große Änderung sein könnte oder sogar nicht für Ihr Projekt geeignet ist, aber haben Sie darüber nachgedacht , den Push erst durchzuführen, wenn Sie bereits über die Daten verfügen ? Auf diese Weise müssen Sie die Ansicht nur einmal zeichnen, und die Benutzererfahrung wird ebenfalls besser - der Push wird bereits geladen verschoben.
Die Art und Weise, wie Sie dies tun, besteht darin, dass
UITableView
didSelectRowAtIndexPath
Sie asynchron nach den Daten fragen. Sobald Sie die Antwort erhalten haben, führen Sie die Übergabe manuell durch und übergeben die Daten an Ihren viewController inprepareForSegue
. In der Zwischenzeit möchten Sie möglicherweise einen Aktivitätsindikator anzeigen. Überprüfen Sie zum einfachen Laden des Indikators https://github.com/jdg/MBProgressHUDquelle