我有一个标签显示一个数字,我想将其更改为更高的数字,但是我想为它添加一些亮点。 我想让数字增加到更高的数字,并且容易进入曲线,因此它会加速然后变慢。 这个答案显示了如何使它增加(第二个答案,而不是接受的答案),但我宁愿为它制作动画,这样我也可以使它稍微增大,然后再缩小,以及缓和曲线。 how to do a running score animation in iphone sdk
任何想法如何最好地实现这一目标? 感谢
开始/结束编号将由用户输入,我希望它在相同的时间内递增结束编号。因此,如果我已经开始10结束100或开始10结束1000我希望它在5秒钟内计算结束数字。
答案 0 :(得分:66)
我实际上只是为了这个名为UICountingLabel的那个类:
http://github.com/dataxpress/UICountingLabel
它允许您指定是否要将计数模式设置为线性,缓入,缓出或缓入/缓出。轻松进入/退出开始缓慢计数,加速,然后慢慢结束 - 所有这些都在您指定的任何时间内完成。
它目前不支持根据当前值设置标签的实际字体大小,但如果它是一个需要的功能,我可以添加支持。我的布局中的大多数标签没有很大的增长或缩小的空间,所以我不确定你想如何使用它。但是,它的行为与普通标签完全相同,因此您也可以自行更改字体大小。
答案 1 :(得分:17)
您可以使用GCD将延迟转移到后台线程。
以下是值动画的示例(10秒内从1到100)
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];
});
}
});
答案 2 :(得分:11)
@ malex在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)"
}
}
}
}
但是,我强烈建议简单地downloading this class from GitHub并将其拖入您的项目中,我已经使用它,因为我的代码中的时间似乎不适合更低/更高的计数数字。这个课程很棒,看起来很棒。请参阅this medium article以供参考。
答案 3 :(得分:4)
你可以使用一个标志来查看它是否必须上升或下降。 而不是for循环,使用while循环。 通过这种方式,你创建了一个继续前进的循环,所以你必须找到一种方法来阻止它,例如。按下按钮。
答案 4 :(得分:2)
在这里您就不会阻塞睡眠! 将其粘贴到UILabel上,它将根据当前显示的值上下计数,首先清除非数字。 如果需要,可以从Double调整为Int或Float。
yourlabel.countAnimation(upto: 100.0)
another simple alternative
extension UILabel {
func countAnimation(upto: Double) {
let from: Double = text?.replace(string: ",", replacement: ".").components(separatedBy: CharacterSet.init(charactersIn: "-0123456789.").inverted).first.flatMap { Double($0) } ?? 0.0
let steps: Int = 20
let duration = 0.350
let rate = duration / Double(steps)
let diff = upto - from
for i in 0...steps {
DispatchQueue.main.asyncAfter(deadline: .now() + rate * Double(i)) {
self.text = "\(from + diff * (Double(i) / Double(steps)))"
}
}
}
}
答案 5 :(得分:1)
如果您想要快速计数的动画,则可以使用递归函数,如下所示:
func updateGems(diff: Int) {
animateIncrement(diff: diff, index: 0, start: 50)
}
func animateIncrement(diff: Int, index: Int, start: Int) {
if index == diff {return}
gemsLabel.text = "\(start + index)"
DispatchQueue.main.asyncAfter(deadline: .now() + 0.002) {
self.animateIncrement(diff: diff, index: index + 1, start: start)
}
}
答案 6 :(得分:0)
我就这样做了:
- (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];
}
答案 7 :(得分:0)
您还可以查看https://github.com/leszek-s/LSCategories
它允许使用单行代码在UILabel中递增/递减数字,如下所示:
[self.label lsAnimateCounterWithStartValue:10 endValue:100 duration:5 completionBlock:nil];
答案 8 :(得分:0)
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")
}
}
不要忘记在此处添加解决方案代码
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")
}
}
}
}
答案 9 :(得分:0)
Swift 4代码
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)"
})
}
})
答案 10 :(得分:0)
Swift 4代码:
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)"
})
}
})
答案 11 :(得分:0)
对于正在寻找线性减速计数器(Swift 5)的用户:
func animateCountLabel(to userCount: Int) {
countLabel.text = " "
let countStart: Int = userCount - 10
let countEnd: Int = userCount
DispatchQueue.global(qos: .default).async { [weak self] in
for i in (countStart...countEnd) {
let delayTime = UInt64(pow(2, (Float(i) - Float(countStart))))
usleep(useconds_t( delayTime * 1300 )) // sleep in microseconds
DispatchQueue.main.async { [weak self] in
self?.countLabel.text = "\(i)"
}
}
}
}
您可以通过根据需要更改静态值来更改起点和降低速度;)