阻止总是返回 nil
Block always returning nil
我正在尝试为 WatchKit 应用录制 UIView
动画。首先,我在没有记录 return 零帧的块的情况下实现了该功能。这是由于 [recorder stop]
在动画完成之前被调用(我认为)。所以,我添加了一个完成块。现在,它从来没有 self.completion
是 YES
。我希望完成块在动画完成时通知我。我在这里错过了什么?
ViewController.m
-(void)runAnimation{
ALBatteryView *batteryView = [[ALBatteryView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
[self.batteryIcon addSubview:batteryView];
recorder.view = _batteryIcon;
[recorder start];
[batteryView setBatteryLevelWithAnimation:YES forValue:[UIDevice currentDevice].batteryLevelInPercentage inPercent:YES];
CGFloat batteryPer = [UBattery batteryLevel];
batteryPercentage.text = [NSString stringWithFormat:@"%.0f%%", batteryPer];
battery = [NSString stringWithFormat:@"%.0f%%", batteryPer];
[batteryView batteryAnaminationWithCompletion:^(BOOL finished){
if (finished){
[recorder stop];
}
}];
}
AlBatteryView.h
@interface ALBatteryView : UIView {
UIView *batteryFill;
}
@property (nonatomic, strong) void(^completion)(BOOL finished);
- (void)setBatteryLevelWithAnimation:(BOOL)isAnimated forValue:(CGFloat)batteryLevel inPercent:(BOOL)inPercent;
- (void)reload;
- (void)batteryAnaminationWithCompletion:(void (^)(BOOL finished))completion;
@end
ALBatteryView.m
- (void)setBatteryLevelWithAnimation:(BOOL)isAnimated forValue:(CGFloat)batteryLevel inPercent:(BOOL)inPercent {
// Declare the newWidth and save the correct battery level
// based on inPercent value
CGFloat newWidth;
CGFloat newBatteryLevel = (inPercent) ? batteryLevel : batteryLevel * 100;
// Set the new width
newWidth = kOnePercent * newBatteryLevel;
// If animated proceed with the animation
// else assign the value without animates
if (isAnimated)
[UIView animateWithDuration:2.0 animations:^{
/* This direct assignment is possible
* using the UIView+ALQuickFrame category
* http://github.com/andrealufino/ALQuickFrame */
batteryFill.width = newWidth;
// Set the color based on battery level
batteryFill.backgroundColor = [self setColorBasedOnBatteryLevel:newBatteryLevel];
if (self.completion) {
self.completion(YES);
}
}];
else
batteryFill.width = newWidth;
}
-(void)batteryAnaminationWithCompletion:(void (^)(BOOL finished))completion{
self.completion = completion;
}
您应该在开始动画之前设置完成块(更改代码顺序)。
那么,你的区块应该被声明为副本。
@property (nonatomic, copy) void(^completion)(BOOL finished);
然后,完全删除(错误命名的)batteryAnaminationWithCompletion 方法,只使用 属性:
batteryView.completion = ...
您需要在调用动画 (setBatteryLevelWithAnimation
) 之前设置块 属性。否则,当您在设置之前尝试访问它时,它将是 nil
。
你也应该直接设置你的块属性,这样会更清楚,因为那是你的-batteryAnaminationWithCompletion
方法所做的(顺便说一下,它应该拼写为"Animation")
来自:
[batteryView batteryAnaminationWithCompletion:^(BOOL finished){
if (finished){
[recorder stop];
}
}];
收件人:
[batteryView setCompletion:^(BOOL finished){
if (finished){
[recorder stop];
}
}];
如果我没看错的话,
当你调用-(void)runAnimation
它在这里调用动画块,动画块将引用目前为 nil 的完成
[batteryView setBatteryLevelWithAnimation:YES forValue:[UIDevice currentDevice].batteryLevelInPercentage inPercent:YES];
如果您认为 animationWithDuration 调用块晚于 2 秒.. 这是错误的.. self.completion 目前为零并且动画的持续时间为 2 秒但 self.completion 不可设置动画并且它提前调用..如果你想等待这个动画完成,你需要某种锁。
[UIView animateWithDuration:2.0 animations:^{
/* This direct assignment is possible
* using the UIView+ALQuickFrame category
* http://github.com/andrealufino/ALQuickFrame */
batteryFill.width = newWidth;
// Set the color based on battery level
batteryFill.backgroundColor = [self setColorBasedOnBatteryLevel:newBatteryLevel];
if (self.completion) {
self.completion(YES);
}
}];
只需在块内部的 animateWithDuration 和 NSLog 之前打印出 NSLog 以查看何时引用 (self.completion)..
如果只有 objective 是动画,则界面过于复杂。根本不需要 属性 块。考虑以下...
// ALBatteryView.h
@interface ALBatteryView : UIView
// up to you, but I'd clean up the animating interface by making a property of this class determine
// how it handles levels in all cases as either absolute or %
@property(assign, nonatomic) BOOL inPercent;
// use naming style like the sdk...
- (void)setBatteryLevel:(CGFloat)batteryLevel animated:(BOOL)animated completion:(void (^)(BOOL))completion;
@end
实施中...
// ALBatteryView.m
@interface ALBatteryView ()
// hide the internal view property here. make it weak, and assign it after [self subView:...
@property(weak,nonatomic) UIView *batteryFill;
@end
仅此一种动画方法,在UIView动画中使用一些技巧,您可以完成所需的一切...
- (void)setBatteryLevel:(CGFloat)batteryLevel animated:(BOOL)animated completion:(void (^)(BOOL))completion {
// one trick is that UIView animation with zero duration just
// changes the animatable properties and calls its completion block
NSTimeInterval duration = (animated)? 2.0 : 0.0;
CGFloat newBatteryLevel = (self.inPercent)? batteryLevel : batteryLevel * 100;
CGFloat newWidth = kOnePercent * newBatteryLevel;
// don't name that color-returning method "set" color. The "set" part happens here...
UIColor *newColor = [self colorBasedOnBatteryLevel:newBatteryLevel];
// now, the animation in just one shot, passing the block parameter
// which has the same signature as the UIView animation block param
[UIView animateWithDuration:duration animations:^{
self.batteryFill.width = newWidth;
self.batteryFill.backgroundColor = newColor;
} completion:completion];
}
我正在尝试为 WatchKit 应用录制 UIView
动画。首先,我在没有记录 return 零帧的块的情况下实现了该功能。这是由于 [recorder stop]
在动画完成之前被调用(我认为)。所以,我添加了一个完成块。现在,它从来没有 self.completion
是 YES
。我希望完成块在动画完成时通知我。我在这里错过了什么?
ViewController.m
-(void)runAnimation{
ALBatteryView *batteryView = [[ALBatteryView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
[self.batteryIcon addSubview:batteryView];
recorder.view = _batteryIcon;
[recorder start];
[batteryView setBatteryLevelWithAnimation:YES forValue:[UIDevice currentDevice].batteryLevelInPercentage inPercent:YES];
CGFloat batteryPer = [UBattery batteryLevel];
batteryPercentage.text = [NSString stringWithFormat:@"%.0f%%", batteryPer];
battery = [NSString stringWithFormat:@"%.0f%%", batteryPer];
[batteryView batteryAnaminationWithCompletion:^(BOOL finished){
if (finished){
[recorder stop];
}
}];
}
AlBatteryView.h
@interface ALBatteryView : UIView {
UIView *batteryFill;
}
@property (nonatomic, strong) void(^completion)(BOOL finished);
- (void)setBatteryLevelWithAnimation:(BOOL)isAnimated forValue:(CGFloat)batteryLevel inPercent:(BOOL)inPercent;
- (void)reload;
- (void)batteryAnaminationWithCompletion:(void (^)(BOOL finished))completion;
@end
ALBatteryView.m
- (void)setBatteryLevelWithAnimation:(BOOL)isAnimated forValue:(CGFloat)batteryLevel inPercent:(BOOL)inPercent {
// Declare the newWidth and save the correct battery level
// based on inPercent value
CGFloat newWidth;
CGFloat newBatteryLevel = (inPercent) ? batteryLevel : batteryLevel * 100;
// Set the new width
newWidth = kOnePercent * newBatteryLevel;
// If animated proceed with the animation
// else assign the value without animates
if (isAnimated)
[UIView animateWithDuration:2.0 animations:^{
/* This direct assignment is possible
* using the UIView+ALQuickFrame category
* http://github.com/andrealufino/ALQuickFrame */
batteryFill.width = newWidth;
// Set the color based on battery level
batteryFill.backgroundColor = [self setColorBasedOnBatteryLevel:newBatteryLevel];
if (self.completion) {
self.completion(YES);
}
}];
else
batteryFill.width = newWidth;
}
-(void)batteryAnaminationWithCompletion:(void (^)(BOOL finished))completion{
self.completion = completion;
}
您应该在开始动画之前设置完成块(更改代码顺序)。
那么,你的区块应该被声明为副本。
@property (nonatomic, copy) void(^completion)(BOOL finished);
然后,完全删除(错误命名的)batteryAnaminationWithCompletion 方法,只使用 属性:
batteryView.completion = ...
您需要在调用动画 (setBatteryLevelWithAnimation
) 之前设置块 属性。否则,当您在设置之前尝试访问它时,它将是 nil
。
你也应该直接设置你的块属性,这样会更清楚,因为那是你的-batteryAnaminationWithCompletion
方法所做的(顺便说一下,它应该拼写为"Animation")
来自:
[batteryView batteryAnaminationWithCompletion:^(BOOL finished){
if (finished){
[recorder stop];
}
}];
收件人:
[batteryView setCompletion:^(BOOL finished){
if (finished){
[recorder stop];
}
}];
如果我没看错的话,
当你调用-(void)runAnimation 它在这里调用动画块,动画块将引用目前为 nil 的完成
[batteryView setBatteryLevelWithAnimation:YES forValue:[UIDevice currentDevice].batteryLevelInPercentage inPercent:YES];
如果您认为 animationWithDuration 调用块晚于 2 秒.. 这是错误的.. self.completion 目前为零并且动画的持续时间为 2 秒但 self.completion 不可设置动画并且它提前调用..如果你想等待这个动画完成,你需要某种锁。
[UIView animateWithDuration:2.0 animations:^{
/* This direct assignment is possible
* using the UIView+ALQuickFrame category
* http://github.com/andrealufino/ALQuickFrame */
batteryFill.width = newWidth;
// Set the color based on battery level
batteryFill.backgroundColor = [self setColorBasedOnBatteryLevel:newBatteryLevel];
if (self.completion) {
self.completion(YES);
}
}];
只需在块内部的 animateWithDuration 和 NSLog 之前打印出 NSLog 以查看何时引用 (self.completion)..
如果只有 objective 是动画,则界面过于复杂。根本不需要 属性 块。考虑以下...
// ALBatteryView.h
@interface ALBatteryView : UIView
// up to you, but I'd clean up the animating interface by making a property of this class determine
// how it handles levels in all cases as either absolute or %
@property(assign, nonatomic) BOOL inPercent;
// use naming style like the sdk...
- (void)setBatteryLevel:(CGFloat)batteryLevel animated:(BOOL)animated completion:(void (^)(BOOL))completion;
@end
实施中...
// ALBatteryView.m
@interface ALBatteryView ()
// hide the internal view property here. make it weak, and assign it after [self subView:...
@property(weak,nonatomic) UIView *batteryFill;
@end
仅此一种动画方法,在UIView动画中使用一些技巧,您可以完成所需的一切...
- (void)setBatteryLevel:(CGFloat)batteryLevel animated:(BOOL)animated completion:(void (^)(BOOL))completion {
// one trick is that UIView animation with zero duration just
// changes the animatable properties and calls its completion block
NSTimeInterval duration = (animated)? 2.0 : 0.0;
CGFloat newBatteryLevel = (self.inPercent)? batteryLevel : batteryLevel * 100;
CGFloat newWidth = kOnePercent * newBatteryLevel;
// don't name that color-returning method "set" color. The "set" part happens here...
UIColor *newColor = [self colorBasedOnBatteryLevel:newBatteryLevel];
// now, the animation in just one shot, passing the block parameter
// which has the same signature as the UIView animation block param
[UIView animateWithDuration:duration animations:^{
self.batteryFill.width = newWidth;
self.batteryFill.backgroundColor = newColor;
} completion:completion];
}