【问题标题】:Block always returning nil阻止总是返回 nil
【发布时间】:2015-07-13 23:22:31
【问题描述】:

我正在尝试为 WatchKit 应用录制 UIView 动画。首先,我在没有记录返回零帧的块的情况下实现了该功能。这是由于在动画完成之前调用了[recorder stop](我认为)。所以,我添加了一个完成块。现在,它永远不会有self.completionYES。我希望完成块在动画完成时通知我。我在这里错过了什么?

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

【问题讨论】:

  • 你为什么不想使用[UIView animateWithDuration:2.0 animations:^{ } completion: (BOOL finished) { }]; 和代表??

标签: objective-c objective-c-blocks


【解决方案1】:

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

你也应该直接设置你的块属性,它会更清楚,因为这就是你的 -batteryAnaminationWithCompletion 方法所做的(顺便说一句,它应该拼写为“动画”) 来自:

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

收件人:

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

【讨论】:

    【解决方案2】:

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

    那么,你的块应该被声明为副本。

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

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

    batteryView.completion = ...
    

    【讨论】:

    • @Oyeoj 的评论效果很好。我不会跟着你的方式。
    【解决方案3】:

    如果我没看错的话

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

      [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)..

    【讨论】:

      【解决方案4】:

      如果唯一的目标是动画,界面就会过于复杂。根本不需要块属性。考虑以下...

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

      【讨论】:

        猜你喜欢
        • 2017-03-28
        • 2015-03-05
        • 2014-08-25
        • 2016-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多