【问题标题】:Fade/dissolve when changing UIImageView's image更改 UIImageView 的图像时淡入淡出/溶解
【发布时间】:2011-11-30 03:07:26
【问题描述】:

与其创建两个UIImageViews,不如简单地更改一个视图的image 似乎是合乎逻辑的。如果我这样做,是否有两个图像之间的淡入淡出/交叉溶解而不是即时切换?

【问题讨论】:

    标签: ios objective-c swift cocoa-touch uiimageview


    【解决方案1】:

    是的,您所说的绝对正确,这就是这样做的方法。我写了这个方法并总是用它来淡入我的图像。我为此处理CALayer。您需要为此导入核心动画。

    + (void)fadeInLayer:(CALayer *)l
    {
        CABasicAnimation *fadeInAnimate   = [CABasicAnimation animationWithKeyPath:@"opacity"];
        fadeInAnimate.duration            = 0.5;
        fadeInAnimate.repeatCount         = 1;
        fadeInAnimate.autoreverses        = NO;
        fadeInAnimate.fromValue           = [NSNumber numberWithFloat:0.0];
        fadeInAnimate.toValue             = [NSNumber numberWithFloat:1.0];
        fadeInAnimate.removedOnCompletion = YES;
        [l addAnimation:fadeInAnimate forKey:@"animateOpacity"];
        return;
    }
    

    您可以对淡出图像执行相反的操作。淡出之后。您只需将其从超级视图(UIImageView)中删除。 [imageView removeFromSuperview].

    【讨论】:

    • 我在UIImageView中设置图片后添加了这段代码,启动动画时似乎有闪烁。
    【解决方案2】:

    编辑:@algal below 有更好的解决方案。

    另一种方法是使用预定义的 CAAnimation 过渡:

    CATransition *transition = [CATransition animation];
    transition.duration = 0.25;
    transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    transition.type = kCATransitionFade;
    transition.delegate = self;
    [self.view.layer addAnimation:transition forKey:nil];
    view1.hidden = YES;
    view2.hidden = NO;
    

    查看 Apple 的 View Transitions 示例项目:https://developer.apple.com/library/content/samplecode/ViewTransitions/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007411

    【讨论】:

    • 完美!我将此代码的一部分添加到 SDWebImage 的 UIImageView (WebCache) 类别中。
    • @OutMan 你最好使用下面由 user577888 提供的新方法。
    • 我也在以这种方式在 SDWebImages 回调之后设置我的图像,但由于某种原因,在我的 CollectionViewCell 中,第一个单元格的图像最初是来自最后一个可见单元格的图像,如果我的所有单元格都更新了。我猜这是某种缓存presentationLayer之类的东西?!?有什么想法吗?
    • 支持编辑“下面的@algal 有更好的解决方案。”
    【解决方案3】:

    使用新的基于块的UIKit animation 方法可以更简单。

    假设以下代码在视图控制器中,而您要交叉溶解的 UIImageView 是 self.view 的子视图,可通过属性self.imageView 寻址,那么您只需要:

    UIImage * toImage = [UIImage imageNamed:@"myname.png"];
    [UIView transitionWithView:self.imageView
            duration:5.0f
            options:UIViewAnimationOptionTransitionCrossDissolve
            animations:^{
      self.imageView.image = toImage;
    } completion:nil]
    

    完成。

    要在 Swift 中完成,就像这样:

    let toImage = UIImage(named:"myname.png")
    UIView.transitionWithView(self.imageView,
                              duration:5,
                              options: UIViewAnimationOptions.TransitionCrossDissolve, animations: { self.imageView.image = toImage }, completion: nil)
    

    Swift 3、4 和 5

    let toImage = UIImage(named:"myname.png")
    UIView.transition(with: self.imageView,
                      duration: 0.3,
                      options: .transitionCrossDissolve,
                      animations: { self.imageView.image = toImage },
                      completion: nil)
    

    【讨论】:

    • 其他答案正确但已过时(和/或过于冗长)。
    • 不需要在超级视图上做过渡。我设法通过将 UIImageView 本身指定为目标来完成这项工作。
    • @user577888 只是一件小事。您应该将 nil 传递给空的完成块。块不是函数指针(由 ^ 表示)并且像 Objective-c 对象一样操作。如果传递 NULL,编译器会将其解释为 0 或 (void *)0。如果由于某种原因,transitionWithView:... 的底层代码在未检查其有效性的情况下不小心将消息发送到完成块(例如 [完成副本]),这将导致错误。所以在设置一个块为空时,你应该总是使用objective-c的nil。
    • @Mr.T NULL 和 nil 的值相同。两者都定义为 0,并且都不会改变。因此,在您想或可以使用 nil 的任何地方使用 NULL 都是安全的。
    • @Mr.T 我的观点是,使用其中一个永远不会导致崩溃,因为在编译的代码级别它们是相同的,并且出于实际原因,总是会如此。一般来说,抑制编译器警告是个好主意,但告诉人们使用 NULL 而不是 nil 是错误的,这是完全错误的。
    【解决方案4】:

    您也可以将淡入功能封装在一个子类中,这样您就可以将其用作通用的 UIImageView,如下例所示:

    IMMFadeImageView *fiv=[[IMMFadeImageView alloc] initWithFrame:CGRectMake(10, 10, 50, 50)];
    [self.view addSubview:fiv];
    fiv.image=[UIImage imageNamed:@"initialImage.png"];
    fiv.image=[UIImage imageNamed:@"fadeinImage.png"];  // fades in
    

    一个可能的实现如下。

    注意:您在 setImage: 函数中实际实现淡入的方式可能会发生变化,并且可能是该问题的其他答案中描述的其他优秀示例之一 — 创建一个额外的 on-the -fly UIImageView 就像我在这里所做的那样,在您的特定情况下可能是不可接受的开销

    IMMFadeImageView.h

    #import <UIKit/UIKit.h>
    
    @interface IMMFadeImageView : UIImageView
    @property (nonatomic,assign) float fadeDuration;
    @end
    

    IMMFadeImageView.m

    #import "IMMFadeImageView.h"
    
    @implementation IMMFadeImageView
    @synthesize fadeDuration;
    
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            self.fadeDuration=1;
        }
        return self;
    }
    -(void)setImage:(UIImage *)newImage{
    
        if(!self.image||self.fadeDuration<=0){
            super.image=newImage;
        } else {
            UIImageView *iv=[[UIImageView alloc] initWithFrame:self.bounds];
            iv.contentMode=self.contentMode;
            iv.image=super.image;
            iv.alpha=1;
            [self addSubview:iv];
            super.image=newImage;
            [UIView animateWithDuration:self.fadeDuration delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
                iv.alpha=0;
            } completion:^(BOOL finished) {
                [iv removeFromSuperview];
            }];
        }
    }
    

    上述代码依赖于一些假设(包括在您的 XCode 项目中启用 ARC),仅用作概念证明,并且为了清晰和集中,它通过省略重要的不相关来保持相关性代码。请不要盲目地复制粘贴。

    【讨论】:

      【解决方案5】:

      我需要无限期地重复过渡。这需要大量的试验和错误,但我终于得到了我正在寻找的最终结果。这些是用于向 UITableViewCell 中的 UIImageView 添加图像动画的代码 sn-ps。

      以下是相关代码:

      @interface SomeViewController ()
      @property(nonatomic, strong) NSMutableArray *imagesArray;
      @property(nonatomic, assign) NSInteger varietyImageAnimationIndex;
      @property(nonatomic, assign) BOOL varietyImagesAnimated;
      @end
      
      @implementation SomeViewController
      @synthesize imagesArray;
      @synthesize varietyImageAnimationIndex;
      @synthesize varietyImagesAnimated;
      ...
      
      // NOTE: Initialize the array of images in perhaps viewDidLoad method.
      
      -(void)animateImages
      {
          varietyImageAnimationIndex++;
      
          [UIView transitionWithView:varietyImageView
                            duration:2.0f
                             options:UIViewAnimationOptionTransitionCrossDissolve
                          animations:^{
                              varietyImageView.image = [imagesArray objectAtIndex:varietyImageAnimationIndex % [imagesArray count]];
                          } completion:^(BOOL finished) {
                              [self animateImages];
                          }];
      }
      
      - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
      {
         ...
              [cell.imageView setImage:[imagesArray objectAtIndex:0]];
      
      
              [self setVarietyImageView:cell.imageView];
      
              if (! varietyImagesAnimated)
              {
                  varietyImagesAnimated = YES;
                  [self animateImages];
              }
      
          ...
          return cell;
      }
      

      【讨论】:

      • 您好,我想知道您是否可以在您的 [self setVarietyImageView:cell.imageView] 上添加更多信息;方法,因为我可以让最后一个单元格动画或全部动画但非常快,谢谢
      • 嗨,Gav,“varietyImageView”是视图控制器中的弱引用,因此我可以分配给 cell.imageView 并稍后在方法“animateImages”中引用它。 @property(nonatomic, weak) UIImageView *varietyImageView;您可以通过在“transitionWithView”中设置更短的“持续时间”来使图像旋转得更快。希望这就是您要问的。
      • 嗨,克里斯托弗,谢谢你的回复,我想知道“setVarietyImageView:”我认为它是某种允许重用的 void 方法,因为我只能为最后一个单元格设置动画。再次感谢您的回复,祝您一切顺利
      • 此方法是使用标准“属性”声明生成的。例如,@property(nonatomic, weak) UIImageView *varietyImageView。这是一个“弱”引用,因为它引用了另一个局部变量(表格单元格的 imageView)。我本可以引用表格单元格并访问方法 animateImages 中的 cell.imageView。我希望这会有所帮助。
      【解决方案6】:

      这是我认为最短的方法。创建一个 UIView 动画并将其提交到您的 imageView。

      [UIView beginAnimations:nil context:NULL];
      [UIView setAnimationDuration:0.5];
      [myImageView setAlpha:0.0];
      [UIView commitAnimations];
      

      【讨论】:

        【解决方案7】:

        通过使用highlightedImage 属性,这可以变得更简单一些。这是 Swift 3 中的一个示例。首先设置正常和突出显示的图像:

        let imageView = UIImageView(image: UIImage(named: "image"), highlightedImage: UIImage(named: "highlightedImage"))
        

        当你想在那些动画之间切换时:

        UIView.transition(with: imageView, duration: 0.3, options: .transitionCrossDissolve, animations: { self.imageView.isHighlighted = !self.imageView.isHighlighted}, completion: .none)
        

        【讨论】:

          【解决方案8】:

          对于 Swift 3.0.1:

          UIView.transition(with: self.imageView,
                        duration:0.5,
                        options: .transitionCrossDissolve,
                        animations: { self.imageView.image = newImage },
                        completion: nil)
          

          参考:https://gist.github.com/licvido/bc22343cacfa3a8ccf88

          【讨论】:

            【解决方案9】:

            在玩弄UIView.transition() 并遇到.transitionCrossDissolve 选项的问题后(​​我试图在一个 UIImageView 内对图像进行动画更改,并且在没有动画的情况下立即发生过渡)我发现您只需要再添加一个选项,即让您在视图内更改属性的动画(Swift 4.2):

            UIView.transition(with: self.imageView,
                               duration: 1,
                               options: [.allowAnimatedContent, .transitionCrossDissolve],
                               animations: { self.imageView.image = newImage },
                               completion: nil)
            

            此外:如果您的 imageView 上有任何子视图,它也会被重绘,并且可能会阻止动画。例如,我的 imageView 上有模糊的子视图,在这种情况下动画不起作用。所以我只是改变了我的视图层次结构并将模糊移动到它自己的视图并将它放在 imageView 上。

            【讨论】:

              猜你喜欢
              • 2018-03-17
              • 1970-01-01
              • 1970-01-01
              • 2014-07-06
              • 2012-07-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-11-19
              相关资源
              最近更新 更多