最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

IOS實(shí)現(xiàn)碎片化動(dòng)畫(huà)詳解

 更新時(shí)間:2016年08月12日 14:57:12   投稿:daisy  
在網(wǎng)上看到一個(gè)驚艷的碎片化動(dòng)畫(huà),于是實(shí)現(xiàn)之后拿來(lái)講解一下,有需要的小伙伴們可以參考學(xué)習(xí)哦。

碎片化效果圖

遮罩視圖

在UIView中有一個(gè)maskView屬性,這個(gè)屬性是我們今天實(shí)現(xiàn)動(dòng)畫(huà)的最重要的變量。這個(gè)屬性在iOS8之后開(kāi)始使用,用來(lái)表示視圖的遮罩。什么是遮罩呢?我想了很久都沒(méi)有找到合適的比喻來(lái)介紹這個(gè)。簡(jiǎn)單來(lái)說(shuō),一個(gè)UIView的對(duì)象,可以通過(guò)設(shè)置alpha來(lái)改變這個(gè)視圖的透明度,遮罩的實(shí)現(xiàn)效果也是一樣的。唯一的差別在于前者是通過(guò)修改0~1之間的值來(lái)改變透明效果,作為遮罩的視圖對(duì)象的backgroundColor、alpha、transform等等屬性都會(huì)影響到被遮蓋的視圖的透明效果。

例如下面這段代碼:

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;

遮罩視圖決定了視圖的顯示內(nèi)容

上面的代碼小小的改動(dòng)一下,我們分別修改一下maskViewcontentView的透明度,看看在遮罩透明度改變之后紅色的視圖會(huì)發(fā)生什么變化:


修改透明度.png

通過(guò)實(shí)驗(yàn)我們可以看到修改視圖自身的透明度或者修改maskView的透明度達(dá)成的效果是一樣的。換句話(huà)說(shuō),遮蓋視圖對(duì)于視圖自身的影響直接決定在透明度和顯示尺寸這兩個(gè)可視的屬性。

那么,遮蓋視圖除了alpha屬性外,還有什么屬性影響了視圖本身的顯示效果呢?

顏色

上面的透明度效果得出了一個(gè)結(jié)論。視圖本身的顯示效果取決于maskView的透明程度。在顏色不含透明空間的時(shí)候,視圖是不存在透明效果的。但是假設(shè)我們?cè)O(shè)置遮罩視圖的顏色透明度時(shí):

maskView.backgroundColor = [UIColor colorWithWhite: 1 alpha: 0.5]; //任意顏色

顯示的效果跟直接設(shè)置alpha = 0.5的效果是一樣的。在繪制像素到屏幕上中可以獲知顏色渲染和alpha屬性存在的關(guān)聯(lián)

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的子視圖對(duì)遮罩效果的影響,我們需要排除遮罩視圖自身的干擾,因此maskView的背景顏色要設(shè)置成透明色


子視圖對(duì)于遮罩的影響

可以看到,在遮罩自身透明的情況下,子視圖也可以實(shí)現(xiàn)部分遮罩視圖的效果。因此如果我們改變這些子視圖的透明度的時(shí)候,遮罩效果也同樣會(huì)發(fā)生改變

動(dòng)畫(huà)實(shí)現(xiàn)

回到上面展示的動(dòng)畫(huà)效果,我們可以看到圖片被分割成多個(gè)長(zhǎng)方形的小塊逐漸消失。其中,垂直方向分為上下兩份,橫向大概有15份左右。因此我們需要現(xiàn)在maskView上面添加2*15個(gè)子視圖,均勻分布。為了保證在動(dòng)畫(huà)的時(shí)候我們能依次實(shí)現(xiàn)子視圖的隱藏,我們需要給子視圖加上標(biāo)識(shí):

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;

那么在動(dòng)畫(huà)開(kāi)始的時(shí)候,我們需要依次遍歷maskView上面的所有子視圖,并且讓他們依次執(zhí)行動(dòng)畫(huà):

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

我們?cè)趯?shí)現(xiàn)動(dòng)畫(huà)的同時(shí),都應(yīng)該考慮如何把動(dòng)畫(huà)封裝出來(lái)方便以后復(fù)用。上面的碎片化動(dòng)畫(huà)完全可以作為UIViewcategory進(jìn)行封裝,以此來(lái)降低入侵性,實(shí)現(xiàn)低耦合的要求:

#define LXDMAXDURATION 1.2
#define LXDMINDURATION .2
#define LXDMULTIPLED .25

@interface UIView (LXDFadeAnimation)

/*!
 * @brief 視圖是否隱藏
 */
@property (nonatomic, assign, readonly) BOOL isFade;
/*!
 * @brief 是否處在動(dòng)畫(huà)中
 */
@property (nonatomic, assign, readonly) BOOL isFading;
/*!
 * @brief 垂直方塊個(gè)數(shù)。默認(rèn)為3
 */
@property (nonatomic, assign) NSInteger verticalCount;
/*!
 * @brief 水平方塊個(gè)數(shù)。默認(rèn)為18
 */
@property (nonatomic, assign) NSInteger horizontalCount;
/*!
 * @brief 方塊動(dòng)畫(huà)之間的間隔0.2~1.2。默認(rèn)0.7
 */
@property (nonatomic, assign) NSTimeInterval intervalDuration;
/*!
 * @brief 每個(gè)方塊隱藏的動(dòng)畫(huà)時(shí)間0.05~0.3,最多為動(dòng)畫(huà)時(shí)長(zhǎng)的25%。默認(rèn)為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中聲明的所有屬性編譯器都不會(huì)自動(dòng)綁定gettersetter方法,這意味著我們需要重寫(xiě)這兩種方法,而且還不能使用下劃線(xiàn)+變量名的方式直接訪(fǎng)問(wèn)變量。因此我們需要導(dǎo)入objc/runtime.h文件使用動(dòng)態(tài)時(shí)提供的objc_associateObject機(jī)制來(lái)為視圖動(dòng)態(tài)增加屬性:

- (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

有了碎片化隱藏視圖的動(dòng)畫(huà),同樣需要一個(gè)還原的動(dòng)畫(huà)效果:

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

現(xiàn)在我們還要考慮一個(gè)問(wèn)題:假設(shè)用戶(hù)點(diǎn)擊某張圖片的時(shí)候就根據(jù)視圖是否隱藏狀態(tài)來(lái)開(kāi)始隱藏/顯示的動(dòng)畫(huà),當(dāng)用戶(hù)多次點(diǎn)擊的時(shí)候,我們應(yīng)該判斷是否已經(jīng)處在動(dòng)畫(huà)狀態(tài),如果是,那么不繼續(xù)執(zhí)行動(dòng)畫(huà)代碼。另外,在動(dòng)畫(huà)開(kāi)始之前,我們需要把標(biāo)識(shí)動(dòng)畫(huà)狀態(tài)的isFading設(shè)為YES,但是由于每個(gè)方塊隱藏都存在一個(gè)動(dòng)畫(huà),動(dòng)畫(huà)的結(jié)束時(shí)間應(yīng)該怎么判斷呢?已知fadeView的個(gè)數(shù)是count,那么當(dāng)最后一個(gè)方塊隱藏即是第count個(gè)動(dòng)畫(huà)完成的時(shí)候,整個(gè)碎片化動(dòng)畫(huà)就結(jié)束了。所以我們需要借助一個(gè)臨時(shí)變量來(lái)記錄:

__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(); }
  }
}];
//......

得到動(dòng)畫(huà)結(jié)束的時(shí)間后,我們就可以增加一個(gè)block提供給調(diào)用者在動(dòng)畫(huà)結(jié)束時(shí)進(jìn)行其他的處理。

輪播碎片動(dòng)畫(huà)

在知道了碎片動(dòng)畫(huà)的實(shí)現(xiàn)之后,我要做一個(gè)酷炫的廣告輪播頁(yè)。同樣采用category的方式來(lái)實(shí)現(xiàn)?,F(xiàn)在放上效果圖:


廣告輪播頁(yè)

那么實(shí)現(xiàn)一個(gè)廣告頁(yè)輪播需要哪些步驟呢?

    1、在當(dāng)前動(dòng)畫(huà)的圖片下面插入一個(gè)UIImageView來(lái)展示下一張圖片。如果可以,盡量復(fù)用這個(gè)imageView

    2、添加UIPageControl來(lái)標(biāo)識(shí)圖片的下標(biāo)

因此我提供了一個(gè)接口傳入圖片數(shù)組執(zhí)行動(dòng)畫(huà):

// 獲取動(dòng)態(tài)綁定臨時(shí)展示的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一開(kāi)始我加在執(zhí)行動(dòng)畫(huà)的imageView上面,但是在動(dòng)畫(huà)執(zhí)行到一半的時(shí)候,pageControl也會(huì)隨著局部隱藏動(dòng)畫(huà)隱藏起來(lái)。因此根據(jù)imageView當(dāng)前的坐標(biāo)重新計(jì)算出合適的尺寸范圍:

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

由于每次圖片碎片化動(dòng)畫(huà)執(zhí)行完成之后,都需要再次執(zhí)行相同的碎片動(dòng)畫(huà)代碼。而動(dòng)畫(huà)結(jié)束是通過(guò)block執(zhí)行,即我們需要在block中嵌套使用同一個(gè)block,因此首先我們需要把這段執(zhí)行代碼聲明成一個(gè)block變量。另外,需要一個(gè)聲明一個(gè)idx在每次碎片動(dòng)畫(huà)完成的時(shí)候更新圖片,用__block修飾來(lái)讓我們?cè)诨卣{(diào)中修改這個(gè)值:

- (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并執(zhí)行動(dòng)畫(huà)
  objc_setAssociatedObject(self, kCompleteBlockKey, complete, OBJC_ASSOCIATION_COPY_NONATOMIC);
  [self animateFadeWithComplete: ^{
    if (!self.stop) {
      complete();
    }
  }];
}

// 更新展示的圖片,并且返回下一次要展示的圖片下標(biāo)
- (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的機(jī)制保存了這個(gè)完成回調(diào)的block,這個(gè)是必要的。假設(shè)你不喜歡把更新圖片的代碼封裝出來(lái),直接把這一步驟放到上面的complete聲明中,依舊還是要?jiǎng)討B(tài)保存起來(lái),否則這個(gè)block執(zhí)行到第三次圖片碎片的時(shí)候就會(huì)被釋放從而導(dǎo)致崩潰

別忘了在每次圖片切換完成之后,將所有的子視圖遮罩還原,并且更新圖片顯示

- (void)reverseWithoutAnimate
{
  if (self.isFading) {
    NSLog(@"It's animating!");
    return;
  }
  for (UIView * subview in self.maskView.subviews) {
    subview.alpha = 1;
  }
}

總結(jié)

以上就是關(guān)于IOS實(shí)現(xiàn)碎片化動(dòng)畫(huà)的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家開(kāi)發(fā)IOS動(dòng)畫(huà)的時(shí)候能有所幫助。

相關(guān)文章

  • IOS游戲開(kāi)發(fā)之五子棋OC版

    IOS游戲開(kāi)發(fā)之五子棋OC版

    五子棋是大家比較熟悉的一款小游戲,相信很多人用多種語(yǔ)言寫(xiě)過(guò)五子棋小游戲,本文試著用OC實(shí)現(xiàn)了一下,在這里給大家分享一下。有需要的可以參考學(xué)習(xí)。
    2016-08-08
  • iOS中延時(shí)執(zhí)行的幾種方式比較及匯總

    iOS中延時(shí)執(zhí)行的幾種方式比較及匯總

    這篇文章主要給大家介紹了關(guān)于iOS中延時(shí)執(zhí)行的幾種方式比較及匯總,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-02-02
  • iOS-GCD詳解及簡(jiǎn)單使用

    iOS-GCD詳解及簡(jiǎn)單使用

    這篇文章主要介紹了iOS-GCD詳解的相關(guān)資料,并附簡(jiǎn)單的實(shí)例代碼,幫助大家學(xué)習(xí)理解此部分的知識(shí),需要的朋友可以參考下
    2016-11-11
  • IOS之WebSocket框架Starscream案例詳解

    IOS之WebSocket框架Starscream案例詳解

    這篇文章主要介紹了IOS之WebSocket框架Starscream案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • iOS開(kāi)發(fā)實(shí)現(xiàn)下載器的基本功能(1)

    iOS開(kāi)發(fā)實(shí)現(xiàn)下載器的基本功能(1)

    這篇文章主要為大家詳細(xì)介紹了iOS開(kāi)發(fā)實(shí)現(xiàn)下載器基本功能的相關(guān)資料,感興趣的小伙伴們可以參考一下
    2016-07-07
  • iOS?Lotusoot模塊化工具應(yīng)用的動(dòng)態(tài)思路

    iOS?Lotusoot模塊化工具應(yīng)用的動(dòng)態(tài)思路

    項(xiàng)目的不斷更迭,導(dǎo)致項(xiàng)目越來(lái)越大,越來(lái)越臃腫,為了讓項(xiàng)目更加條理,需要對(duì)項(xiàng)目進(jìn)行模塊化處理,為了減少模塊之間的耦合,于是就有了Lotusoot這個(gè)工具
    2022-08-08
  • iOS 中事件的響應(yīng)鏈和傳遞鏈

    iOS 中事件的響應(yīng)鏈和傳遞鏈

    iOS事件鏈有兩條:事件的響應(yīng)鏈;Hit-Testing事件的傳遞鏈。這篇文章主要介紹了iOS 中事件的響應(yīng)鏈和傳遞鏈,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12
  • 詳解iOS使用Keychain中的kSecClassGenericPassword存儲(chǔ)數(shù)據(jù)

    詳解iOS使用Keychain中的kSecClassGenericPassword存儲(chǔ)數(shù)據(jù)

    iOS設(shè)備中的Keychain是一個(gè)安全的存儲(chǔ)容器,本篇文章主要介紹了iOS使用Keychain中的kSecClassGenericPassword存儲(chǔ)數(shù)據(jù),有興趣的可以了解一下。
    2016-11-11
  • iOS制作framework靜態(tài)庫(kù)圖文教程

    iOS制作framework靜態(tài)庫(kù)圖文教程

    這篇文章主要為大家詳細(xì)介紹了iOS制作framework靜態(tài)庫(kù)圖文教程,感興趣的小伙伴們可以參考一下
    2016-08-08
  • IOS 中UIApplication詳解及實(shí)例

    IOS 中UIApplication詳解及實(shí)例

    這篇文章主要介紹了IOS 中UIApplication詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-05-05

最新評(píng)論

齐河县| 南靖县| 保亭| 沾化县| 宝应县| 宜黄县| 涟水县| 礼泉县| 襄垣县| 乌拉特中旗| 平顶山市| 民勤县| 乌兰察布市| 濮阳县| 讷河市| 安溪县| 水城县| 康定县| 无棣县| 临潭县| 合川市| 洪湖市| 夏河县| 三明市| 长丰县| 平凉市| 建宁县| 丘北县| 商河县| 水富县| 南乐县| 古蔺县| 阿勒泰市| 徐闻县| 龙泉市| 大邑县| 咸阳市| 怀远县| 武城县| 新丰县| 曲阳县|