高效圖片輪播消痛,兩個(gè)ImageView實(shí)現(xiàn)

導(dǎo)語

在不少項(xiàng)目中,都會有圖片輪播這個(gè)功能油讯,現(xiàn)在網(wǎng)上關(guān)于圖片輪播的框架層出不窮,千奇百怪延欠,筆者根據(jù)自己的思路陌兑,用兩個(gè)imageView也實(shí)現(xiàn)了圖片輪播,這里說說筆者的主要思路以及大概步驟由捎,具體代碼請看這里兔综,如果覺得好用,請獻(xiàn)上你的star

該輪播框架的優(yōu)勢

1.文件少狞玛,代碼簡潔
2.不依賴任何其他第三方庫
3.同時(shí)支持本地圖片及網(wǎng)絡(luò)圖片
4.自帶圖片下載與緩存

實(shí)際使用

我們先看demo软驰,代碼如下


運(yùn)行效果


輪播實(shí)現(xiàn)步驟

接下來,筆者將從各方面逐一分析

層級結(jié)構(gòu)

最底層是一個(gè)UIView心肪,上面有一個(gè)UIScrollView以及UIPageControl锭亏,scrollView上有兩個(gè)UIImageView,imageView寬高 = scrollview寬高 = view寬高


輪播原理

假設(shè)輪播控件的寬度為x高度為y蒙畴,我們設(shè)置scrollview的contentSize.width為3x贰镣,并讓scrollview的水平偏移量為x呜象,既顯示最中間內(nèi)容

scrollView.contentSize = CGSizeMake(3x, y);
scrollView.contentOffset = CGPointMake(x, 0);

將imageView添加到scrollview內(nèi)容視圖的中間位置


接下來使用代理方法scrollViewDidScroll來監(jiān)聽scrollview的滾動膳凝,定義一個(gè)枚舉變量來記錄滾動的方向

typedef enum{
  DirecNone,
  DirecLeft,
  DirecRight
} Direction;

@property (nonatomic, assign) Direction direction;

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  self.direction = scrollView.contentOffset.x >x? DirecLeft : DirecRight;
}

使用KVO來監(jiān)聽direction屬性值的改變

[self addObserver:self forKeyPath:@"direction" options:NSKeyValueObservingOptionNew context:nil];

判斷滾動的方向,當(dāng)偏移量大于x恭陡,表示左移蹬音,則將otherImageView加在右邊,偏移量小于x休玩,表示右移著淆,則將otherImageView加在左邊


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
   //self.currIndex表示當(dāng)前顯示圖片的索引劫狠,self.nextIndex表示將要顯示圖片的索引
  //_images為圖片數(shù)組
  if(change[NSKeyValueChangeNewKey] == change[NSKeyValueChangeOldKey]) return;
  if ([change[NSKeyValueChangeNewKey] intValue] == DirecRight) {
    self.otherImageView.frame = CGRectMake(0, 0, self.width, self.height);
    self.nextIndex = self.currIndex - 1;
    if (self.nextIndex < 0) self.nextIndex = _images.count – 1;
  } else if ([change[NSKeyValueChangeNewKey] intValue] == DirecLeft){
    self.otherImageView.frame = CGRectMake(CGRectGetMaxX(_currImageView.frame), 0, self.width, self.height);
    self.nextIndex = (self.currIndex + 1) % _images.count;
  }
  self.otherImageView.image = self.images[self.nextIndex];
}

通過代理方法scrollViewDidEndDecelerating來監(jiān)聽滾動結(jié)束,結(jié)束后永部,會變成以下兩種情況


此時(shí)独泞,scrollview的偏移量為0或者2x,我們通過代碼再次將scrollview的偏移量設(shè)置為x苔埋,并將currImageView的圖片修改為otherImageView的圖片

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
  [self pauseScroll];
}

- (void)pauseScroll {
  self.direction = DirecNone;//清空滾動方向
    //判斷最終是滾到了右邊還是左邊
  int index = self.scrollView.contentOffset.x / x;
  if (index == 1) return; //等于1表示最后沒有滾動懦砂,返回不做任何操作
  self.currIndex = self.nextIndex;//當(dāng)前圖片索引改變
  self.pageControl.currentPage = self.currIndex;
  self.currImageView.frame = CGRectMake(x, 0, x, y);
  self.currImageView.image = self.otherImageView.image;
  self.scrollView.contentOffset = CGPointMake(x, 0);
}

那么我們看到的還是currImageView,只不過展示的是下一張圖片组橄,如圖荞膘,又變成了最初的效果


自動滾動

輪播的功能實(shí)現(xiàn)了,接下來添加定時(shí)器讓它自動滾動玉工,相當(dāng)簡單

- (void)startTimer {
   //如果只有一張圖片羽资,則直接返回,不開啟定時(shí)器
   if (_images.count <= 1) return;
   //如果定時(shí)器已開啟遵班,先停止再重新開啟
   if (self.timer) [self stopTimer];
   self.timer = [NSTimer timerWithTimeInterval:self.time target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
   [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)nextPage {
    //動畫改變scrollview的偏移量就可以實(shí)現(xiàn)自動滾動
  [self.scrollView setContentOffset:CGPointMake(self.width * 2, 0) animated:YES];
}
注意

setContentOffset:animated:方法執(zhí)行完畢后不會調(diào)用scrollview的scrollViewDidEndDecelerating方法屠升,但是會調(diào)用scrollViewDidEndScrollingAnimation方法,因此我們要在該方法中調(diào)用pauseScroll

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
  [self pauseScroll];
}

拖拽時(shí)停止自動滾動

當(dāng)我們手動拖拽圖片時(shí)费奸,需要停止自動滾動弥激,此時(shí)我們只需要讓定時(shí)器失效就行了,當(dāng)停止拖拽時(shí)愿阐,重新啟動定時(shí)器

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  [self.timer invalidate];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
  [self startTimer];
}

加載圖片

實(shí)際開發(fā)中微服,我們很少會輪播本地圖片,大部分都是服務(wù)器獲取的缨历,也有可能既有本地圖片以蕴,也有網(wǎng)絡(luò)圖片,那要如何來加載呢辛孵?

定義4個(gè)屬性
NSArray imageArray:暴露在.h文件中丛肮,外界將要加載的圖片或路徑數(shù)組賦值給該屬性
NSMutableArray images:用來存放圖片的數(shù)組
NSMutableDictionary imageDic:用來緩存圖片的字典,key為URL
NSMutableDictionary operationDic:用來保存下載操作的字典魄缚,key為URL

判斷外界傳入的是圖片還是路徑宝与,如果是圖片,直接加入圖片數(shù)組中冶匹,如果是路徑习劫,先添加一個(gè)占位圖片,然后根據(jù)路徑去下載圖片

_images = [NSMutableArray array];
for (int i = 0; i < imageArray.count; i++) {
    if ([imageArray[i] isKindOfClass:[UIImage class]]) {
      [_images addObject:imageArray[i]];//如果是圖片嚼隘,直接添加到images中
    } else if ([imageArray[i] isKindOfClass:[NSString class]]){
      [_images addObject:[UIImage imageNamed:@"placeholder"]];//如果是路徑诽里,添加一個(gè)占位圖片到images中
      [self downloadImages:i];  //下載網(wǎng)絡(luò)圖片
    }
  }

下載圖片,先從緩存中取飞蛹,如果有谤狡,則替換之前的占位圖片灸眼,如果沒有,去沙盒中取墓懂,如果有焰宣,替換占位圖片,并添加到緩存中捕仔,如果沒有宛徊,開啟異步線程下載

- (void)downloadImages:(int)index {
  NSString *key = _imageArray[index];
  //從字典緩存中取圖片
  UIImage *image = [self.imageDic objectForKey:key];
  if (image) {
    _images[index] = image;//如果圖片存在,則直接替換之前的占位圖片
  }else{
    //字典中沒有從沙盒中取圖片
    NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSString *path = [cache stringByAppendingPathComponent:[key lastPathComponent]];
    NSData *data = [NSData dataWithContentsOfFile:path];
    if (data) {
             //沙盒中有逻澳,替換占位圖片闸天,并加入字典緩存中
      image = [UIImage imageWithData:data];
      _images[index] = image;
      [self.imageDic setObject:image forKey:key];
    }else{
       //字典沙盒都沒有,下載圖片
      NSBlockOperation *download = [self.operationDic objectForKey:key];//查看下載操作是否存在
      if (!download) {//不存在
        //創(chuàng)建一個(gè)隊(duì)列斜做,默認(rèn)為并發(fā)隊(duì)列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        //創(chuàng)建一個(gè)下載操作
        download = [NSBlockOperation blockOperationWithBlock:^{
          NSURL *url = [NSURL URLWithString:key];
          NSData *data = [NSData dataWithContentsOfURL:url];
           if (data) {
                        //下載完成后苞氮,替換占位圖片,存入字典并寫入沙盒瓤逼,將下載操作從字典中移除掉
            UIImage *image = [UIImage imageWithData:data];
            [self.imageDic setObject:image forKey:key];
            self.images[index] = image;
                        //如果只有一張圖片笼吟,需要在主線程主動去修改currImageView的值
            if (_images.count == 1) [_currImageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
            [data writeToFile:path atomically:YES];
            [self.operationDic removeObjectForKey:key]; 
            }
        }];
        [queue addOperation:download];
        [self.operationDic setObject:download forKey:key];//將下載操作加入字典
      }
    }
  }
}

監(jiān)聽圖片點(diǎn)擊

當(dāng)圖片被點(diǎn)擊的時(shí)候,我們往往需要執(zhí)行某些操作霸旗,因此需要監(jiān)聽圖片的點(diǎn)擊贷帮,思路如下

1.定義一個(gè)block屬性暴露給外界void(^imageClickBlock)(NSInteger index)
(不會block的可以用代理,或者看這里
2.設(shè)置currImageView的userInteractionEnabled為YES
3.給currImageView添加一個(gè)點(diǎn)擊的手勢
4.在手勢方法里調(diào)用block诱告,并傳入圖片索引

結(jié)束語

上面是筆者的主要思路以及部分代碼撵枢,需要源碼的請前往筆者的github下載,https://github.com/codingZero/XRCarouselView精居,記得獻(xiàn)上你的星星哦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锄禽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子靴姿,更是在濱河造成了極大的恐慌沃但,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佛吓,死亡現(xiàn)場離奇詭異宵晚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)维雇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門淤刃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谆沃,你說我怎么就攤上這事钝凶∫敲ⅲ” “怎么了唁影?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵耕陷,是天一觀的道長。 經(jīng)常有香客問我据沈,道長哟沫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任锌介,我火速辦了婚禮嗜诀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘孔祸。我一直安慰自己隆敢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布崔慧。 她就那樣靜靜地躺著拂蝎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惶室。 梳的紋絲不亂的頭發(fā)上温自,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音皇钞,去河邊找鬼悼泌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛夹界,可吹牛的內(nèi)容都是我干的馆里。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼可柿,長吁一口氣:“原來是場噩夢啊……” “哼也拜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起趾痘,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤慢哈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后永票,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卵贱,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年侣集,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了键俱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡世分,死狀恐怖编振,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情臭埋,我是刑警寧澤踪央,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布臀玄,位于F島的核電站,受9級特大地震影響畅蹂,放射性物質(zhì)發(fā)生泄漏健无。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一液斜、第九天 我趴在偏房一處隱蔽的房頂上張望累贤。 院中可真熱鬧,春花似錦少漆、人聲如沸臼膏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讶请。三九已至,卻和暖如春屎媳,著一層夾襖步出監(jiān)牢的瞬間夺溢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工烛谊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留风响,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓丹禀,卻偏偏與公主長得像状勤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子双泪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫持搜、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,024評論 4 62
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,506評論 25 707
  • val fileword =sc.textFile(path,1) .map(x=>(x.split(",")(2...
    scottzcw閱讀 908評論 0 0
  • UIButton同設(shè)置文字和圖片時(shí)默認(rèn)是左圖片右邊文字焙矛,如果需要左文字右圖片或者上圖片下文字就要設(shè)置他們的偏移量葫盼,...
    jay_den閱讀 10,544評論 0 10
  • 跳操跳得滿身是汗,4歲禎禎問我村斟,媽媽是不是醬油贫导,我說是汗水,她準(zhǔn)備去拿毛巾幫忙我擦汗蟆盹,然后對我說媽媽孩灯,累不累?呵呵...
    米芽禎羲閱讀 198評論 0 1