J'ai une étiquette indiquant un numéro et je souhaite le remplacer par un chiffre plus élevé, mais j'aimerais ajouter un peu de lumière .. __J'aimerais que le numéro augmente progressivement jusqu'au numéro le plus élevé. une courbe d’inaccélération afin d’accélérer puis d’accélérer puis ralentir .. Cette réponse montre comment l’incrémenter (la 2ème réponse, pas la réponse acceptée) mais je l’animerais plutôt pour pouvoir l’augmenter légèrement. puis réduisez à nouveau ainsi que la courbe de facilité d’installation . comment faire une animation de partition en cours d’exécution dans iphone sdk
Des idées sur la meilleure façon de réaliser cela? Merci
Les numéros de début/fin seront saisis par l'utilisateur et je veux qu'il incrémente le numéro de fin dans le même temps. Donc, si je dois commencer 10 fin 100 ou commencer 10 000 fin, je veux qu'il compte jusqu'à la fin nombre en 5 secondes.
Vous pouvez utiliser un drapeau pour voir s’il doit monter ou descendre . Au lieu d’une boucle for, utilisez une boucle while . De cette manière, vous créez une boucle qui continue, vous devez donc trouver un moyen de l'arrêter aussi, fe par un bouton appuyez sur.
J'ai en fait fait une telle classe juste pour cela appelé UICountingLabel:
http://github.com/dataxpress/UICountingLabel
Il vous permet de spécifier si vous souhaitez que le mode de comptage soit linéaire, facile à entrer, facile à utiliser, ou facile à entrer/sortir. Facilité d’entrée/sortie commence à compter lentement, s’accélère puis s’achève lentement, le tout dans le temps que vous spécifiez.
Il ne prend actuellement pas en charge la définition de la taille de police réelle de l'étiquette en fonction de la valeur actuelle, bien que je puisse l'ajouter si c'est une fonctionnalité en demande. La plupart des étiquettes de mes mises en page n'ont pas beaucoup de place pour se développer ou se réduire, donc je ne sais pas comment vous voulez l'utiliser. Cependant, il se comporte totalement comme une étiquette normale, vous pouvez donc également modifier vous-même la taille de la police.
Vous pouvez utiliser GCD pour décaler les retards vers les threads d'arrière-plan.
Voici l'exemple d'animation de valeur (de 1 à 100 en 10 secondes)
float animationPeriod = 10;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
for (int i = 1; i < 101; i ++) {
usleep(animationPeriod/100 * 1000000); // sleep in microseconds
dispatch_async(dispatch_get_main_queue(), ^{
yourLabel.text = [NSString stringWithFormat:@"%d", i];
});
}
});
Voici la réponse de @ malex dans Swift 3.
func incrementLabel(to endValue: Int) {
let duration: Double = 2.0 //seconds
DispatchQueue.global().async {
for i in 0 ..< (endValue + 1) {
let sleepTime = UInt32(duration/Double(endValue) * 1000000.0)
usleep(sleepTime)
DispatchQueue.main.async {
self.myLabel.text = "\(i)"
}
}
}
}
Cependant, je recommande fortement de simplement télécharger cette classe à partir de GitHub et de la faire glisser dans votre projet. Je me suis servie de cette classe car le timing de mon code ne semble pas s’ajuster correctement pour les nombres plus faibles/plus élevés. Cette classe fonctionne très bien et est très belle. Voir cet article moyen pour référence.
Swift 4 Code
let animationPeriod: Float = 1
DispatchQueue.global(qos: .default).async(execute: {
for i in 1..<10000)! {
usleep(useconds_t(animationPeriod / 10 * 10000)) // sleep in microseconds
DispatchQueue.main.async(execute: {
self.lblCounter.text = "\(i)"
})
}
})
Xcode 9.2, Swift 4
class LoadingProcess {
let minValue: Int
let maxValue: Int
var currentValue: Int
private let progressQueue = DispatchQueue(label: "ProgressView")
private let semaphore = DispatchSemaphore(value: 1)
init (minValue: Int, maxValue: Int) {
self.minValue = minValue
self.currentValue = minValue
self.maxValue = maxValue
}
private func delay(stepDelayUsec: useconds_t, completion: @escaping ()->()) {
usleep(stepDelayUsec)
DispatchQueue.main.async {
completion()
}
}
func simulateLoading(toValue: Int, step: Int = 1, stepDelayUsec: useconds_t? = 10_000,
valueChanged: @escaping (_ currentValue: Int)->(),
completion: ((_ currentValue: Int)->())? = nil) {
semaphore.wait()
progressQueue.sync {
if currentValue <= toValue && currentValue <= maxValue {
usleep(stepDelayUsec!)
DispatchQueue.main.async {
valueChanged(self.currentValue)
self.currentValue += step
self.semaphore.signal()
self.simulateLoading(toValue: toValue, step: step, stepDelayUsec: stepDelayUsec, valueChanged: valueChanged, completion: completion)
}
} else {
self.semaphore.signal()
completion?(currentValue)
}
}
}
func finish(step: Int = 1, stepDelayUsec: useconds_t? = 10_000,
valueChanged: @escaping (_ currentValue: Int)->(),
completion: ((_ currentValue: Int)->())? = nil) {
simulateLoading(toValue: maxValue, step: step, stepDelayUsec: stepDelayUsec, valueChanged: valueChanged, completion: completion)
}
}
let loadingProcess = LoadingProcess(minValue: 0, maxValue: 100)
loadingProcess.simulateLoading(toValue: 80, valueChanged: { currentValue in
// Update views
})
DispatchQueue.global(qos: .background).async {
print("Start loading data")
sleep(5)
print("Data loaded")
loadingProcess.finish(valueChanged: { currentValue in
// Update views
}) { _ in
print("End")
}
}
N'oubliez pas de ajouter le code de solution ici
import UIKit
class ViewController: UIViewController {
weak var counterLabel: UILabel!
weak var progressView: UIProgressView!
override func viewDidLoad() {
super.viewDidLoad()
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .fill
stackView.distribution = .fillProportionally
stackView.spacing = 8
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 80).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -80).isActive = true
let label = UILabel()
label.textAlignment = .center
label.text = "0"
label.font = UIFont.systemFont(ofSize: 46)
stackView.addArrangedSubview(label)
counterLabel = label
let progressView = UIProgressView()
progressView.trackTintColor = .lightGray
progressView.progressTintColor = .blue
progressView.layer.cornerRadius = 4
progressView.clipsToBounds = true
progressView.heightAnchor.constraint(equalToConstant: 8).isActive = true
stackView.addArrangedSubview(progressView)
self.progressView = progressView
let button = UIButton()
button.setTitle("Start", for: .normal)
button.addTarget(self, action: #selector(startButtonTapped), for: .touchUpInside)
button.setTitleColor(.blue, for: .normal)
button.heightAnchor.constraint(equalToConstant: 30).isActive = true
stackView.addArrangedSubview(button)
}
@objc func startButtonTapped() {
sample()
}
private func setProcess(currentValue: Int) {
let value = 0.01 * Float(currentValue)
self.counterLabel?.text = "\(currentValue)"
self.progressView?.setProgress(value, animated: true)
print("\(currentValue)")
}
func sample() {
let loadingProcess = LoadingProcess(minValue: 0, maxValue: 100)
loadingProcess.simulateLoading(toValue: 80, valueChanged: { currentValue in
self.setProcess(currentValue: currentValue)
})
DispatchQueue.global(qos: .background).async {
print("Start loading data")
sleep(5)
print("Data loaded")
loadingProcess.finish(valueChanged: { currentValue in
self.setProcess(currentValue: currentValue)
}) { _ in
print("end")
}
}
}
}
Voici comment je l'ai fait:
- (void)setupAndStartCounter:(CGFloat)duration {
NSUInteger step = 3;//use your own logic here to define the step.
NSUInteger remainder = numberYouWantToCount%step;//for me it was 30
if (step < remainder) {
remainder = remainder%step;
}
self.aTimer = [self getTimer:[self getInvocation:@selector(animateLabel:increment:) arguments:[NSMutableArray arrayWithObjects:[NSNumber numberWithInteger:remainder], [NSNumber numberWithInteger:step], nil]] timeInterval:(duration * (step/(float) numberYouWantToCountTo)) willRepeat:YES];
[self addTimerToRunLoop:self.aTimer];
}
- (void)animateLabel:(NSNumber*)remainder increment:(NSNumber*)increment {
NSInteger finish = finalValue;
if ([self.aLabel.text integerValue] <= (finish) && ([self.aLabel.text integerValue] + [increment integerValue]<= (finish))) {
self.aLabel.text = [NSString stringWithFormat:@"%lu",(unsigned long)([self.aLabel.text integerValue] + [increment integerValue])];
}else{
self.aLabel.text = [NSString stringWithFormat:@"%lu",(unsigned long)([self.aLabel.text integerValue] + [remainder integerValue])];
[self.aTimer invalidate];
self.aTimer = nil;
}
}
#pragma mark -
#pragma mark Timer related Functions
- (NSTimer*)getTimer:(NSInvocation *)invocation timeInterval:(NSTimeInterval)timeInterval willRepeat:(BOOL)willRepeat
{
return [NSTimer timerWithTimeInterval:timeInterval invocation:invocation repeats:willRepeat];
}
- (NSInvocation*)getInvocation:(SEL)methodName arguments:(NSMutableArray*)arguments
{
NSMethodSignature *sig = [self methodSignatureForSelector:methodName];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:self];
[invocation setSelector:methodName];
if (arguments != nil)
{
id arg1 = [arguments objectAtIndex:0];
id arg2 = [arguments objectAtIndex:1];
[invocation setArgument:&arg1 atIndex:2];
[invocation setArgument:&arg2 atIndex:3];
}
return invocation;
}
- (void)addTimerToRunLoop:(NSTimer*)timer
{
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
Swift 4 Code:
let animationPeriod: Float = 1
DispatchQueue.global(qos: .default).async(execute: {
for i in 1..<Int(endValue) {
usleep(useconds_t(animationPeriod / 10 * 10000)) // sleep in microseconds
DispatchQueue.main.async(execute: {
self.lbl.text = "\(i+1)"
})
}
})
Vous pouvez également vérifier https://github.com/leszek-s/LSCategories
Cela permet d'incrémenter/décrémenter un nombre dans UILabel avec une seule ligne de code comme celle-ci:
[self.label lsAnimateCounterWithStartValue:10 endValue:100 duration:5 completionBlock:nil];