碎片化後果圖
遮罩視圖
在UIView中有一個maskView屬性,這個屬性是我們明天完成動畫的最主要的變量。這個屬性在IOS8以後開端應用,用來表現視圖的遮罩。甚麼是遮罩呢?我想了良久都沒有找到適合的比方來引見這個。簡略來講,一個UIView
的對象,可以經由過程設置alpha
來轉變這個視圖的通明度,遮罩的完成後果也是一樣的。獨一的差異在於前者是經由過程修正0~1之間的值來轉變通明後果,作為遮罩的視圖對象的backgroundColor
、alpha
、transform
等等屬性都邑影響到被隱瞞的視圖的通明後果。
例以下面這段代碼:
UIView * viewContainer = [[UIView alloc] initWithFrame: CGRectMake(0, 0, 200, 200)]; viewContainer.backgroundColor = [UIColor blueColor]; UIView * contentView = [[UIView alloc] initWithFrame: CGRectMake(20, 20, 160, 160)]; contentView.backgroundColor = [UIColor redColor]; [viewContainer addSubview: contentView]; UIView * maskView = [[UIView alloc] initWithFrame: CGRectMake(100, 100, 35, 80)]; maskView.backgroundColor = [UIColor yellowColor]; contentView.maskView = maskView;
遮罩視圖決議了視圖的顯示內容
下面的代碼小小的修改一下,我們分離修正一下maskView
和contentView
的通明度,看看在遮罩通明度轉變以後白色的視圖會產生甚麼變更:
修正通明度.png
經由過程試驗我們可以看到修正視圖本身的通明度或許修正maskView
的通明度殺青的後果是一樣的。換句話說,隱瞞視圖關於視圖本身的影響直接決議在通明度和顯示尺寸這兩個可視的屬性。
那末,隱瞞視圖除alpha屬性外,還有甚麼屬性影響了視圖自己的顯示後果呢?
色彩
下面的通明度後果得出了一個結論。視圖自己的顯示後果取決於maskView的通明水平。在色彩不含通明空間的時刻,視圖是不存在通明後果的。然則假定我們設置遮罩視圖的色彩通明度時:
maskView.backgroundColor = [UIColor colorWithWhite: 1 alpha: 0.5]; //隨意率性色彩
顯示的後果跟直接設置alpha = 0.5
的後果是一樣的。在繪制像素到屏幕上中可以獲知色彩襯著和alpha屬性存在的聯系關系
maskView的子視圖
maskView.backgroundColor = [UIColor clearColor]; UIView * sub1 = [[UIView alloc] initWithFrame: CGRectMake(0, 0, 20, 34)]; sub1.backgroundColor = [UIColor blackColor]; UIView * sub2 = [[UIView alloc] initWithFrame: CGRectMake(15, 18, 33, 40)]; sub2.backgroundColor = [UIColor blackColor]; [maskView addSubview: sub1]; [maskView addSubview: sub2];
要懂得maskView
的子視圖對遮罩後果的影響,我們須要消除遮罩視圖本身的攪擾,是以maskView
的配景色彩要設置成通明色
子視圖關於遮罩的影響
可以看到,在遮罩本身通明的情形下,子視圖也能夠完成部門遮罩視圖的後果。是以假如我們轉變這些子視圖的通明度的時刻,遮罩後果也異樣會產生轉變
動畫完成
回到下面展現的動畫後果,我們可以看到圖片被朋分成多個長方形的小塊逐步消逝。個中,垂直偏向分為高低兩份,橫向年夜概有15份閣下。是以我們須要如今maskView
下面添加2*15個子視圖,平均散布。為了包管在動畫的時刻我們能順次完成子視圖的隱蔽,我們須要給子視圖加上標識:
UIView * maskView = [[UIView alloc] initWithFrame: contentView.bounds]; const NSInteger horizontalCount = 15; const NSInteger verticalCount = 2; const CGFloat fadeWidth = CGRectGetWidth(maskView.frame) / horizontalCount; const CGFloat fadeHeight = CGRectGetHeight(maskView.frame) / verticalCount; for (NSInteger line = 0; line < horizontalCount; line ++) { for (NSInteger row = 0; row < verticalCount; row++) { CGRect frame = CGRectMake(line*fadeWidth, row*fadeHeight, fadeWidth, fadeHeight); UIView * fadeView = [[UIView alloc] initWithFrame: frame]; fadeView.tag = [self viewTag: line*verticalCount+row]; fadeView.backgroundColor = [UIColor whiteColor]; [maskView addSubview: fadeView]; } } contentView.maskView = maskView;
那末在動畫開端的時刻,我們須要順次遍歷maskView
下面的一切子視圖,而且讓他們順次履行動畫:
for (NSInteger line = 0; line < horizontalCount; line ++) { for (NSInteger row = 0; row < verticalCount; row++) { NSInteger idx = line*verticalCount+row; UIView * fadeView = [contentView.maskView viewWithtag: [self viewWithtag: idx]; [UIView animateWithDuration: fadeDuration delay: interval*idx options: UIViewAnimationOptionCurveLinear animations: ^{ fadeView.alpha = 0; } completion: nil]; } }
我們在完成動畫的同時,都應當斟酌若何把動畫封裝出來便利今後復用。下面的碎片化動畫完整可以作為UIView
的category
停止封裝,以此來下降入侵性,完成低耦合的請求:
#define LXDMAXDURATION 1.2 #define LXDMINDURATION .2 #define LXDMULTIPLED .25 @interface UIView (LXDFadeAnimation) /*! * @brief 視圖能否隱蔽 */ @property (nonatomic, assign, readonly) BOOL isFade; /*! * @brief 能否處在動畫中 */ @property (nonatomic, assign, readonly) BOOL isFading; /*! * @brief 垂直方塊個數。默許為3 */ @property (nonatomic, assign) NSInteger verticalCount; /*! * @brief 程度方塊個數。默許為18 */ @property (nonatomic, assign) NSInteger horizontalCount; /*! * @brief 方塊動畫之間的距離0.2~1.2。默許0.7 */ @property (nonatomic, assign) NSTimeInterval intervalDuration; /*! * @brief 每一個方塊隱蔽的動畫時光0.05~0.3,最多為動畫時長的25%。默許為0.175 */ @property (nonatomic, assign) NSTimeInterval fadeAnimationDuration; - (void)configurateWithVerticalCount: (NSInteger)verticalCount horizontalCount: (NSInteger)horizontalCount interval: (NSTimeInterval)interval duration: (NSTimeInterval)duration; - (void)reverseWithComplete: (void(^)(void))complete; - (void)animateFadeWithComplete: (void(^)(void))complete; - (void)reverseWithoutAnimate; @end
在IOS中,在category中聲明的一切屬性編譯器都不會主動綁定getter
和setter
辦法,這意味著我們須要重寫這兩種辦法,並且還不克不及應用下劃線+變量名的方法直接拜訪變量。是以我們須要導入objc/runtime.h
文件應用靜態時供給的objc_associateObject
機制來為視圖靜態增長屬性:
- (BOOL)isFade { return [objc_getAssociatedObject(self, kIsFadeKey) boolValue]; } // other getAssociatedObject method - (void)setIsFade: (BOOL)isFade { objc_setAssociatedObject(self, kIsFadeKey, @(isFade), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } // other setAssociatedObject method
有了碎片化隱蔽視圖的動畫,異樣須要一個復原的動畫後果:
NSInteger fadeCount = self.verticalCount * self.horizontalCount; for (NSInteger idx = fadeCount - 1; idx >= 0; idx--) { UIView * subview = [self.maskView viewWithtag: [self subViewTag: idx]]; [UIView animateWithDuration: self.fadeAnimationDuration delay: self.intervalDuration * (fadeCount - 1 - idx) options: UIViewAnimationOptionCurveLinear animations: ^{ subview.alpha = 1; } completion: nil]; }
如今我們還要斟酌一個成績:假定用戶點擊某張圖片的時刻就依據視圖能否隱蔽狀況來開端隱蔽/顯示的動畫,當用戶屢次點擊的時刻,我們應當斷定能否曾經處在動畫狀況,假如是,那末不持續履行動畫代碼。別的,在動畫開端之前,我們須要把標識動畫狀況的isFading
設為YES
,然則因為每一個方塊隱蔽都存在一個動畫,動畫的停止時光應當怎樣斷定呢?已知fadeView
的個數是count
,那末當最初一個方塊隱蔽等於第count
個動畫完成的時刻,全部碎片化動畫就停止了。所以我們須要借助一個暫時變量來記載:
__block NSInteger timeCount = 0; //...... [UIView animateWithDuration: self.fadeAnimationDuration delay: self.intervalDuration * (fadeCount - 1 - idx) options: UIViewAnimationOptionCurveLinear animations: ^{ subview.alpha = 1; } completion: ^(BOOL finished) { if (++timeCount == fadeCount) { self.isFade = NO; self.isFading = NO; if (complete) { complete(); } } }]; //......
獲得動畫停止的時光後,我們便可以增長一個block
供給給挪用者在動畫停止時停止其他的處置。
輪播碎片動畫
在曉得了碎片動畫的完成以後,我要做一個酷炫的告白輪播頁。異樣采取category
的方法來完成。如今放上後果圖:
告白輪播頁
那末完成一個告白頁輪播須要哪些步調呢?
1、在以後動畫的圖片上面拔出一個UIImageView
來展現下一張圖片。假如可以,盡可能復用這個imageView
2、添加UIPageControl
來標識圖片的下標
是以我供給了一個接口授入圖片數組履行動畫:
// 獲得靜態綁定暫時展現的UIImageView - (UIImageView *)associateTempBannerWithImage: (UIImage *)image { UIImageView * tempBanner = objc_getAssociatedObject(self, kTempImageKey); if (!tempBanner) { tempBanner = [[UIImageView alloc] initWithFrame: self.frame]; objc_setAssociatedObject(self, kTempImageKey, tempBanner, OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self.superview insertSubview: tempBanner belowSubview: self]; } tempBanner.image = image; return tempBanner; }
另外,pageControl
一開端我加在履行動畫的imageView
下面,然則在動畫履行到一半的時刻,pageControl
也會跟著部分隱蔽動畫隱蔽起來。是以依據imageView
以後的坐標從新盤算出適合的尺寸規模:
- (void)associatePageControlWithCurrentIdx: (NSInteger)idx { UIPageControl * pageControl = objc_getAssociatedObject(self, kPageControlKey); if (!pageControl) { pageControl = [[UIPageControl alloc] initWithFrame: CGRectMake(self.frame.origin.x, CGRectGetHeight(self.frame) - 37 + self.frame.origin.y, CGRectGetWidth(self.frame), 37)]; [self.superview addSubview: pageControl]; pageControl.numberOfPages = self.bannerImages.count; objc_setAssociatedObject(self, kPageControlKey, pageControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } pageControl.currentPage = idx; }
因為每次圖片碎片化動畫履行完成以後,都須要再次履行雷同的碎片動畫代碼。而動畫停止是經由過程block
履行,即我們須要在block
中嵌套應用統一個block
,是以起首我們須要把這段履行代碼聲明成一個block變量。別的,須要一個聲明一個idx
在每次碎片動畫完成的時刻更新圖片,用__block
潤飾來讓我們在回調中修正這個值:
- (void)fadeBanner NSParameterAssert(self.superview); UIImageView * tempBanner = [self associateTempBannerWithImage: [UIImage imageNamed: self.bannerImages[1]]]; self.stop = NO; __block NSInteger idx = 0; __weak typeof(self) weakSelf = self; [self associatePageControlWithCurrentIdx: idx]; void (^complete)() = ^{ NSInteger updateIndex = [weakSelf updateImageWithCurrentIndex: ++idx tempBanner: tempBanner]; idx = updateIndex; [weakSelf associatePageControlWithCurrentIdx: idx]; }; // 保留block並履行動畫 objc_setAssociatedObject(self, kCompleteBlockKey, complete, OBJC_ASSOCIATION_COPY_NONATOMIC); [self animateFadeWithComplete: ^{ if (!self.stop) { complete(); } }]; } // 更新展現的圖片,而且前往下一主要展現的圖片下標 - (NSInteger)updateImageWithCurrentIndex: (NSInteger)idx tempBanner: (UIImageView *)tempBanner { if (idx >= self.bannerImages.count) { idx = 0; } self.image = [UIImage imageNamed: self.bannerImages[idx]]; [self reverseWithoutAnimate]; NSInteger nextIdx = idx + 1; if (nextIdx >= self.bannerImages.count) { nextIdx = 0; } tempBanner.image = [UIImage imageNamed: self.bannerImages[nextIdx]]; [self animateFadeWithComplete: ^{ if (!self.stop) { void (^complete)() = objc_getAssociatedObject(self, kCompleteBlockKey); complete(); } }]; return idx; }
代碼中須要留意的是,我在下面應用objc_Associate
的機制保留了這個完成回調的block
,這個是需要的。假定你不愛好把更新圖片的代碼封裝出來,直接把這一步調放到下面的complete
聲明中,照舊照樣要靜態保留起來,不然這個block
履行到第三次圖片碎片的時刻就會被釋放從而招致瓦解
別忘了在每次圖片切換完成以後,將一切的子視圖遮罩復原,而且更新圖片顯示
- (void)reverseWithoutAnimate { if (self.isFading) { NSLog(@"It's animating!"); return; } for (UIView * subview in self.maskView.subviews) { subview.alpha = 1; } }
總結
以上就是關於IOS完成碎片化動畫的全體內容,願望本文的內容對年夜家開辟IOS動畫的時刻能有所贊助。
【IOS完成碎片化動畫詳解】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!