Il modo con rimborso garantito, cemento armato-solido per forzare una vista a disegnare in modo sincrono (prima di tornare al codice chiamante) è configurare le CALayer
interazioni di con la tua UIView
sottoclasse.
Nella tua sottoclasse UIView, crea un displayNow()
metodo che dica al livello di " impostare la rotta per la visualizzazione ", quindi di " renderla tale ":
veloce
/// Redraws the view's contents immediately.
/// Serves the same purpose as the display method in GLKView.
public func displayNow()
{
let layer = self.layer
layer.setNeedsDisplay()
layer.displayIfNeeded()
}
Objective-C
/// Redraws the view's contents immediately.
/// Serves the same purpose as the display method in GLKView.
- (void)displayNow
{
CALayer *layer = self.layer;
[layer setNeedsDisplay];
[layer displayIfNeeded];
}
Implementa anche un draw(_: CALayer, in: CGContext)
metodo che chiamerà il tuo metodo di disegno privato / interno (che funziona poiché ogni UIView
è a CALayerDelegate
) :
veloce
/// Called by our CALayer when it wants us to draw
/// (in compliance with the CALayerDelegate protocol).
override func draw(_ layer: CALayer, in context: CGContext)
{
UIGraphicsPushContext(context)
internalDraw(self.bounds)
UIGraphicsPopContext()
}
Objective-C
/// Called by our CALayer when it wants us to draw
/// (in compliance with the CALayerDelegate protocol).
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
UIGraphicsPushContext(context);
[self internalDrawWithRect:self.bounds];
UIGraphicsPopContext();
}
E crea il tuo internalDraw(_: CGRect)
metodo personalizzato , insieme a fail-safe draw(_: CGRect)
:
veloce
/// Internal drawing method; naming's up to you.
func internalDraw(_ rect: CGRect)
{
// @FILLIN: Custom drawing code goes here.
// (Use `UIGraphicsGetCurrentContext()` where necessary.)
}
/// For compatibility, if something besides our display method asks for draw.
override func draw(_ rect: CGRect) {
internalDraw(rect)
}
Objective-C
/// Internal drawing method; naming's up to you.
- (void)internalDrawWithRect:(CGRect)rect
{
// @FILLIN: Custom drawing code goes here.
// (Use `UIGraphicsGetCurrentContext()` where necessary.)
}
/// For compatibility, if something besides our display method asks for draw.
- (void)drawRect:(CGRect)rect {
[self internalDrawWithRect:rect];
}
E ora chiama semplicemente myView.displayNow()
ogni volta che ne hai davvero bisogno per disegnare (come da una CADisplayLink
richiamata) . Il nostro displayNow()
metodo dirà al CALayer
to displayIfNeeded()
, che richiamerà in modo sincrono nel nostro draw(_:,in:)
e farà il disegno internalDraw(_:)
, aggiornando l'immagine con ciò che è disegnato nel contesto prima di andare avanti.
Questo approccio è simile a quello di @ RobNapier sopra, ma ha il vantaggio di chiamare displayIfNeeded()
in aggiunta a setNeedsDisplay()
, il che lo rende sincrono.
Ciò è possibile perché CALayer
s espone più funzionalità di disegno rispetto a UIView
s: i livelli sono di livello inferiore rispetto alle viste e progettati esplicitamente allo scopo di disegnare altamente configurabili all'interno del layout e (come molte cose in Cocoa) sono progettati per essere utilizzati in modo flessibile ( come classe genitore, o come delegante, o come ponte verso altri sistemi di disegno, o semplicemente da soli). Il corretto utilizzo del CALayerDelegate
protocollo rende tutto questo possibile.
Ulteriori informazioni sulla configurabilità di CALayer
s sono disponibili nella sezione Configurazione di oggetti livello della Guida alla programmazione dell'animazione di base .