เลือน / จางหายเมื่อเปลี่ยนภาพของ UIImageView


160

แทนที่จะสร้างมุมมองทั้งสองUIImageViewsดูเหมือนว่ามีเหตุผลที่จะเพียงแค่เปลี่ยนimageมุมมองหนึ่งมุมมอง ถ้าฉันทำอย่างนั้นจะมีการจางหายไป / ครอสละลายระหว่างภาพสองภาพมากกว่าการเปลี่ยนทันทีหรือไม่?


1
@ kumar-kl การแก้ไขของคุณลบแท็กที่สำคัญ โปรดอย่าทำอย่างนั้น
Eric Aya

1
@ KumarKL แต่ที่นี่ "purpose-c" ไม่ใช่แท็กที่มีลำดับความสำคัญต่ำ! อย่าลบออกเพียงเพื่อเพิ่ม "swift" ซึ่งไม่สมเหตุสมผลจริงๆแล้วมันเป็นแนวเขตแดนที่ป่าเถื่อน ...
Eric Aya

1
@EricD โอเคฉันจะไม่ทำซ้ำเหมือนเดิม ขอบคุณสำหรับความกังวลของคุณ
Kumar KL

คำตอบ:


45

แก้ไข: มีทางออกที่ดีกว่าจาก@algal ด้านล่าง

อีกวิธีในการทำเช่นนี้คือการใช้การเปลี่ยน 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: https://developer.apple.com/library/content/samplecode/ViewTransitions/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007411


1
ที่สมบูรณ์แบบ! ฉันเพิ่มบางส่วนของรหัสนี้ลงในหมวดหมู่ UIImageView (WebCache) ของ SDWebImage
บอทส์

@OutMan คุณอาจจะดีกว่าโดยใช้วิธีการใหม่ด้านล่างโดย user577888
Mirkules

ฉันกำลังตั้งค่ารูปภาพของฉันหลังจากเรียกกลับด้วยวิธี SDWebImages แต่ด้วยเหตุผลบางอย่างใน CollectionViewCell ภาพของเซลล์แรกเซลล์แรกนั้นเป็นเซลล์แรกจากเซลล์สุดท้ายที่มองเห็นถ้าเซลล์ทั้งหมดของฉันอัปเดต ฉันเดาว่ามันเป็นการแคชงานนำเสนอบางสิ่งบางอย่างหรืออะไร?!? ความคิดใด ๆ ที่อาจเป็นได้?
เฟรดริก

โหวตขึ้นสำหรับการแก้ไข "มีวิธีแก้ปัญหาที่ดีกว่าจาก @algal ด้านล่าง"
Rob

581

มันอาจจะง่ายมากโดยใช้บล็อกใหม่ตาม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)

สวิฟท์ 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)

3
คำตอบอื่น ๆ นั้นถูกต้อง แต่ล้าสมัย (และ / หรือ verbose มากเกินไป)
Steven Kramer

3
ไม่จำเป็นต้องทำการเปลี่ยนแปลงในมุมมองระดับสูง ฉันจัดการเพื่อให้งานนี้โดยระบุ UIImageView ตัวเองเป็นเป้าหมาย
Desmond

7
@ user577888 เป็นเพียงสิ่งเล็กน้อย คุณควรผ่านศูนย์ไปยังบล็อกการเสร็จสิ้นที่ว่างเปล่า บล็อกไม่ใช่ตัวชี้ฟังก์ชั่น (ตามที่ระบุโดย ^) และดำเนินการเหมือนวัตถุ -c ถ้าคุณผ่าน NULL คอมไพเลอร์จะตีความว่าเป็น 0 หรือ (void *) 0 หากด้วยเหตุผลบางอย่างรหัสอ้างอิงสำหรับ transitionWithView: ... ส่งข้อความไปยังบล็อกที่เสร็จสมบูรณ์โดยไม่ได้ตั้งใจ (เช่น [สำเนาสมบูรณ์)] โดยไม่ตรวจสอบความถูกต้องของสิ่งนี้จะทำให้เกิดข้อผิดพลาด ดังนั้นคุณควรใช้ศูนย์ของวัตถุประสงค์เสมอเมื่อตั้งค่าบล็อกให้ว่างเปล่า
นาย T

3
@ Mr.T NULL และไม่มีค่าเหมือนกัน ทั้งคู่ถูกกำหนดเป็น 0 และไม่น่าจะมีการเปลี่ยนแปลง ดังนั้นจึงปลอดภัยที่จะใช้ NULL ทุกที่ที่คุณต้องการหรือสามารถใช้เป็นศูนย์
Ashevin

1
@ Mr.T ประเด็นของฉันคือการใช้อีกตัวหนึ่งจะไม่ทำให้เกิดความผิดพลาดเพราะในระดับโค้ดที่คอมไพล์แล้วจะเหมือนกันและด้วยเหตุผลเชิงปฏิบัติจะเป็นเช่นนั้นเสมอ การยับยั้งการเตือนคอมไพเลอร์เป็นความคิดที่ดีโดยทั่วไป แต่การบอกผู้คนว่าการใช้ NULL เป็นข้อผิดพลาดแทนที่จะเป็นเรื่องผิด
ashevin


6

ใช่สิ่งที่คุณพูดถูกต้องอย่างแน่นอนและนั่นคือวิธีที่จะทำ ฉันเขียนวิธีนี้และใช้วิธีนี้ในการลบภาพของฉัน ฉันจัดการกับCALayerสิ่งนี้ คุณต้องนำเข้า Core Animation สำหรับสิ่งนี้

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

คุณสามารถทำสิ่งที่ตรงกันข้ามเพื่อทำให้ภาพจางหายไป หลังจากที่มันจางหายไป คุณเพิ่งลบออกจาก superview (ซึ่งก็คือUIImageView) [imageView removeFromSuperview].


ฉันเพิ่มรหัสนี้หลังจากตั้งค่าภาพเป็น UIImageView ดูเหมือนว่ามีการกะพริบเมื่อเริ่มต้นภาพเคลื่อนไหว
บอทส์

4

คุณยังสามารถทำแพคเกจคุณสมบัติการ fade-in ในคลาสย่อยเพื่อให้คุณสามารถใช้เป็น 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:ฟังก์ชั่นเฟดในฟังก์ชั่นสามารถเปลี่ยนแปลงได้และอาจเป็นหนึ่งในตัวอย่างที่ยอดเยี่ยมอื่น ๆ ที่อธิบายไว้ในคำตอบอื่น ๆ สำหรับคำถามนี้ - การสร้างเพิ่มเติมแบบทันที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];
        }];
    }
}

รหัสข้างต้นอาศัยสมมติฐานสองสามข้อ (รวมถึง ARC ที่เปิดใช้งานในโครงการ XCode ของคุณ) มีวัตถุประสงค์เพื่อพิสูจน์แนวคิดเท่านั้นและเพื่อความชัดเจนและความสนใจโฟกัสนั้นยังคงเกี่ยวข้องโดยไม่ต้องใส่รหัสที่ไม่เกี่ยวข้องสำคัญ โปรดอย่าเพิ่งคัดลอกวางสุ่มสี่สุ่มห้า


3

ฉันต้องการการเปลี่ยนแปลงเพื่อทำซ้ำอย่างไม่มีกำหนด ใช้การทดลองและข้อผิดพลาดมากมายสำหรับอันนี้ แต่ในที่สุดฉันก็ได้รับผลลัพธ์ที่ฉันต้องการ นี่คือโค้ดขนาดสั้นสำหรับการเพิ่มภาพเคลื่อนไหวไปยัง UIImageView ใน UITableViewCell

นี่คือรหัสที่เกี่ยวข้อง:

@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

สวัสดี Gav, "หลากหลายImageView" เป็นการอ้างอิงที่อ่อนแอในตัวควบคุมมุมมองเพื่อให้ฉันสามารถกำหนดให้ cell.imageView และอ้างอิงในภายหลังในวิธีการ 'animateImages' @ คุณสมบัติ (ไม่ใช่เชิงอ่อนแอ) UIImageView * หลากหลายImageView; คุณสามารถทำให้ภาพหมุนได้เร็วขึ้นโดยการตั้ง "ระยะเวลา" ที่สั้นลงใน "transitionWithView" หวังว่านี่คือสิ่งที่คุณถาม
Christopher

สวัสดีคริสโตเฟอร์ขอบคุณสำหรับการตอบกลับฉันสงสัยเกี่ยวกับ "setVarietyImageView:" ฉันถือว่าวิธีการโมฆะบางอย่างที่อนุญาตให้นำกลับมาใช้ใหม่ได้เนื่องจากฉันสามารถสร้างเซลล์สุดท้ายได้เท่านั้น ขอขอบคุณสำหรับการตอบกลับทุกคนที่ดีที่สุดของ Gav
gav

วิธีนี้ถูกสร้างขึ้นโดยใช้การประกาศ "คุณสมบัติ" มาตรฐาน ตัวอย่างเช่น @property (ไม่ใช่เชิงอ่อนแอ, อ่อนแอ) UIImageView * หลากหลายImageView มันคือการอ้างอิง "อ่อนแอ" ตามที่อ้างถึงตัวแปรท้องถิ่นอื่น (imageView ของเซลล์ตาราง) ฉันได้ทำการอ้างอิงไปยังเซลล์ตารางและเข้าถึง cell.imageView ใน animateImages วิธี ฉันหวังว่านี่จะช่วยได้.
Christopher

2

หลังจากเล่นไปเรื่อยๆ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 ของคุณภาพนั้นจะถูกวาดใหม่ด้วยและสามารถป้องกันภาพเคลื่อนไหวได้ ตัวอย่างเช่นฉันมี subview พร้อมเบลอที่ imageView ของฉันและในกรณีที่ภาพเคลื่อนไหวไม่ทำงาน ดังนั้นฉันจึงเปลี่ยนลำดับชั้นการดูของฉันและย้ายเบลอไปยังมุมมองของตัวเองและวางไว้เหนือ imageView


0

นี่คือฉันคิดว่าวิธีที่สั้นที่สุดในการทำมัน สร้างอนิเมชั่น UIView และคอมมิทเข้าไปใน imageView ของคุณ

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

0

โดยใช้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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.