阻止总是返回零

时间:2015-07-13 14:28:07

标签: objective-c objective-c-blocks

我正在尝试为WatchKit应用录制UIView动画。首先,我实现了没有块的函数,它返回零帧被记录。这是因为在动画完成之前调用[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;
}

4 个答案:

答案 0 :(得分:1)

您需要在调用动画(setBatteryLevelWithAnimation)之前设置块属性。否则,在您设置之前尝试访问它时,它将为nil

另外你应该直接设置你的块属性,它会更清楚,因为那是-batteryAnaminationWithCompletion方法的作用(顺便说一下它应拼写为"动画") 从:

  [batteryView batteryAnaminationWithCompletion:^(BOOL finished){
    if (finished){
      [recorder stop];
    }
  }];

要:

  [batteryView setCompletion:^(BOOL finished){
    if (finished){
      [recorder stop];
    }
  }];

答案 1 :(得分:0)

您应该在开始动画之前设置完成块(更改代码顺序)。

然后,您的块应声明为副本。

@property (nonatomic, copy) void(^completion)(BOOL finished);

然后,完全删除(错误命名的)batteryAnaminationWithCompletion方法,并使用属性:

batteryView.completion = ...

答案 2 :(得分:0)

如果我正确阅读,

当你调用 - (void)runAnimation时 它在这里调用动画块,动画块将引用目前为零的完成

  [batteryView setBatteryLevelWithAnimation:YES forValue:[UIDevice currentDevice].batteryLevelInPercentage inPercent:YES];

如果你认为animationWithDuration稍后调用块2秒......这是错误的.. self.completion现在是零,动画的持续时间是2秒但是selfcompletion不是动画的,它会在前面调用..如果你想等完这个动画,你需要某种锁定。

[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,看看是否引用了if(self.completion)..

答案 3 :(得分:0)

如果唯一的目标是动画,那么界面过于复杂。根本不需要块状物。请考虑以下内容......

// 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]; 
}