2013-05-11 23 views
6

Sto lavorando su una barra di avanzamento circolare per la visualizzazione dei risultati del centro di gioco personalizzato e ho tipo di "colpire il muro". Sto lottando con questo per oltre due ore e ancora non riesco a farlo funzionare.Utilizzo della barra di avanzamento circolare con immagine mascherata?

Il fatto è che ho bisogno di una barra di avanzamento circolare, dove sarei in grado di impostare (almeno) l'immagine della traccia (riempimento). Perché il mio riempimento è un gradiente arcobaleno e non riesco a ottenere gli stessi risultati usando solo il codice.

Stavo scherzando con CALayers e metodo drawRect, tuttavia non ho avuto successo. Potresti per favore darmi una piccola guida?

Ecco esempi delle barre di avanzamento circolari: https://github.com/donnellyk/KDGoalBar https://github.com/danielamitay/DACircularProgress

devo solo il riempimento di essere un'immagine mascherata, a seconda dei progressi. Se si potrebbe anche farlo funzionare che il progresso sarebbe stato animato, che sarebbe davvero cool, ma non ho bisogno di aiuto con quella :)

Grazie, Nick

+0

Quindi è mascherare o disegnare un gradiente "arcobaleno" che ti impedisce?O sta mettendo insieme i due pezzi? –

+0

Forse l'ho descritto poco chiaramente, mi spiace per questo - non voglio disegnare una sfumatura, ho un bel riempimento sfumato circolare per la barra di avanzamento fatta in Photoshop, e ho solo bisogno di mascherarlo, quindi è il riempire la barra di avanzamento. Sai cosa intendo? :) –

risposta

23

Che, fondamentalmente, solo bisogno di costruire un percorso che definisce l'area da riempire (es. usando CGPathAddArc), ritaglia il contesto grafico su quel percorso usando CGContextClip e poi disegna la tua immagine.

Ecco un esempio di un metodo drawRect: è possibile utilizzare in una visualizzazione personalizzata:

- (void)drawRect:(CGRect)rect 
{ 
    CGContextRef ctx = UIGraphicsGetCurrentContext(); 

    CGFloat progress = 0.7f; //This would be a property of your view 
    CGFloat innerRadiusRatio = 0.5f; //Adjust as needed 

    //Construct the path: 
    CGMutablePathRef path = CGPathCreateMutable(); 
    CGFloat startAngle = -M_PI_2; 
    CGFloat endAngle = -M_PI_2 + MIN(1.0f, progress) * M_PI * 2; 
    CGFloat outerRadius = CGRectGetWidth(self.bounds) * 0.5f - 1.0f; 
    CGFloat innerRadius = outerRadius * innerRadiusRatio; 
    CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); 
    CGPathAddArc(path, NULL, center.x, center.y, innerRadius, startAngle, endAngle, false); 
    CGPathAddArc(path, NULL, center.x, center.y, outerRadius, endAngle, startAngle, true); 
    CGPathCloseSubpath(path); 
    CGContextAddPath(ctx, path); 
    CGPathRelease(path); 

    //Draw the image, clipped to the path: 
    CGContextSaveGState(ctx); 
    CGContextClip(ctx); 
    CGContextDrawImage(ctx, self.bounds, [[UIImage imageNamed:@"RadialProgressFill"] CGImage]); 
    CGContextRestoreGState(ctx); 
} 

Per farla semplice, ho un paio di cose hard-coded - dovrete, ovviamente, per aggiungere una proprietà per progress e chiamare setNeedsDisplay nel setter. Ciò presuppone anche che nel progetto sia presente un'immagine denominata RadialProgressFill.

Ecco un esempio di quello che sarebbe grosso modo simile:

Screenshot

Spero che tu abbia un'immagine di sfondo più bello. ;)

+0

Spero che ci saranno sempre più persone come te - questo è molto più di quanto mi aspettassi! Grazie mille per la tua risposta dettagliata - Ti ho premiato :) –

+0

@DominikHadl Come e dove cal questo metodo - (void) drawRect: (CGRect) rect –

+0

@omz è davvero utile .. :) –

0

La soluzione proposta da omz ha funzionato in modo rallentato quando stavo cercando di animare la proprietà, quindi ho continuato a cercare. Trovato ottima soluzione: utilizzare framework e wrapper Quartz Core chiamato CADisplayLink. "Questa classe è specificamente realizzati per le animazioni in cui‘i dati è estremamente probabile che a cambiare in ogni fotogramma’. Essa tenta di inviare un messaggio a un obiettivo ogni volta che qualcosa viene ridisegnato alla schermata" Source

E il library che lavora in questo modo.

Modifica: Ma c'è una soluzione più efficiente. Per animare la maschera che viene applicata sul livello con l'immagine. Non so perché non l'ho fatto dall'inizio. CABasicAnimation funziona più velocemente di qualsiasi disegno personalizzato. Qui è la mia realizzazione:

class ProgressBar: UIView { 
    var imageView:UIImageView! 

    var maskLayer: CAShapeLayer! 
    var currentValue: CGFloat = 1 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     updateUI() 
    } 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     updateUI() 
    } 

    func updateUI(){ 
     makeGradient() 
     setUpMask() 
    } 

    func animateCircle(strokeEnd: CGFloat) { 
     let oldStrokeEnd = maskLayer.strokeEnd 
     maskLayer.strokeEnd = strokeEnd 

     //override strokeEnd implicit animation 
     let animation = CABasicAnimation(keyPath: "strokeEnd") 
     animation.duration = 0.5 
     animation.fromValue = oldStrokeEnd 
     animation.toValue = strokeEnd 
     animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) 
     maskLayer.add(animation, forKey: "animateCircle") 

     currentValue = strokeEnd 
    } 

    func setUpMask(){ 

     let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width/2.0, y: frame.size.height/2.0), 
             radius: (frame.size.width - 10)/2, 
             startAngle: -CGFloat.pi/2, 
             endAngle: CGFloat.pi*1.5, 
             clockwise: true) 

     maskLayer = CAShapeLayer() 
     maskLayer.path = circlePath.cgPath 
     maskLayer.fillColor = UIColor.clear.cgColor 
     maskLayer.strokeColor = UIColor.red.cgColor 
     maskLayer.lineWidth = 8.0; 
     maskLayer.strokeEnd = currentValue 
     maskLayer.lineCap = "round" 
     imageView.layer.mask = maskLayer 
    } 

    func makeGradient(){ 
     imageView = UIImageView(image: UIImage(named: "gradientImage")) 
     imageView.frame = bounds 
     imageView.contentMode = .scaleAspectFill 
     addSubview(imageView) 
    } 

} 

A proposito, se si sta cercando di diventare meglio a animazioni, suggerisco un grande libro "IOS Core Animation: Tecniche avanzate Prenota da Nick Lockwood"