MJRefresh幾乎是我們開(kāi)發(fā)工作中必用的一款三方庫(kù)啥繁,它提供一套非常簡(jiǎn)單實(shí)用的拖拽執(zhí)行回調(diào)事件的解決方案菜职。下面是官方提供的框架圖。前言:最近應(yīng)該有很多小伙伴去跳槽面試的吧,相信各位有的已經(jīng)順利收到offer了季惩,而有些則是碰壁了录粱,那么我在這里給大家準(zhǔn)備了相關(guān)面試資料腻格,還有相關(guān)算法資料。想了解的可找我拿
其中最常用的幾個(gè)默認(rèn)視圖類(lèi)分別是:
下拉刷新控件:MJRefreshNormalHeader
上拉加載控件:MJRefreshAutoNormalFooter酬核、MJRefreshBackNormalFooter
左滑加載控件:MJRefreshNormalTrailer
下面將對(duì)這些類(lèi),自上而下地進(jìn)行分析宪睹。
公共基類(lèi)控件
MJRefreshComponent
通過(guò)框架圖可以看出所有視圖都源于同一個(gè)基類(lèi)——MJRefreshComponent
愁茁,它為子類(lèi)提供了公用的屬性和事件,主要有:
- 回調(diào)對(duì)象和回調(diào)方法
- 拖拽狀態(tài)定義和控制
- 通過(guò)KVO亭病,對(duì)事件(控件偏移鹅很、內(nèi)容尺寸、手勢(shì)狀態(tài))添加監(jiān)聽(tīng)(回調(diào)響應(yīng)交給子類(lèi)實(shí)現(xiàn))
- 其他:
- 拖拽百分比
- 根據(jù)拖拽比例自動(dòng)切換透明度
MJRefreshComponent
還為子類(lèi)搭建了基本的邏輯框架:
最后推薦個(gè)我的iOS交流圈:[891 488 181]
'有一個(gè)共同的圈子很重要罪帖,結(jié)識(shí)人脈促煮!里面都是iOS開(kāi)發(fā),全棧發(fā)展整袁,歡迎入駐菠齿,共同進(jìn)步!(群內(nèi)會(huì)免費(fèi)提供一些群主收藏的免費(fèi)學(xué)習(xí)書(shū)籍資料以及整理好的幾百道面試題和答案文檔W肌)
視圖創(chuàng)建
// 1.初始化
- (instancetype)initWithFrame:(CGRect)frame{;}
// 2.準(zhǔn)備工作
- (void)prepare{;}
// 3.視圖即將被父視圖加入
- (void)willMoveToSuperview:(UIView *)newSuperview{
// 滾動(dòng)視圖初始值的記錄
// 一些值的更新
// 監(jiān)聽(tīng)事件的更新
}
// 4.布局
- (void)layoutSubviews{
[self placeSubviews];
}
滾動(dòng)視圖狀態(tài)回調(diào)
// 當(dāng)偏移值發(fā)生變化
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}
// 當(dāng)內(nèi)容大小發(fā)生變化
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}
// 當(dāng)點(diǎn)擊手勢(shì)狀態(tài)發(fā)生變化
- (void)scrollViewPanStateDidChange:(NSDictionary *)change{}
狀態(tài)設(shè)置
// 狀態(tài)設(shè)置
- (void)setState:(MJRefreshState)state{;}
常用方法
// 進(jìn)入刷新?tīng)顟B(tài)
- (void)beginRefreshing{;}
// 結(jié)束刷新?tīng)顟B(tài)
- (void)endRefreshing{;}
其他
// 自動(dòng)切換透明度
- (void)setAutoChangeAlpha:(BOOL)autoChangeAlpha{;}
- (BOOL)isAutoChangeAlpha{;}
- (void)setAutomaticallyChangeAlpha:(BOOL)automaticallyChangeAlpha{;}
// 根據(jù)拖拽進(jìn)度實(shí)時(shí)設(shè)置透明度
- (void)setPullingPercent:(CGFloat)pullingPercent{;}
下拉刷新控件(Header)
下拉刷新控件包含四個(gè)類(lèi):
- MJRefreshHeader
- MJRefreshStateHeader
- MJRefreshNormalHeader
- MJRefreshGifHeader
- MJRefreshStateHeader
MJRefreshHeader
MJRefreshHeader
類(lèi)是一個(gè)包含了完整的下拉刷新功能邏輯的空白視圖绳匀,子類(lèi)MJRefreshStateHeader
和MJRefreshGifHeader
只需要再添加一些額外的圖片和文字,就能提升使用體驗(yàn)和保持代碼的簡(jiǎn)潔易讀性炸客。
實(shí)現(xiàn)過(guò)程
1.初始化
創(chuàng)建視圖疾棵,設(shè)置高度和位置。
- (void)prepare {
[super prepare];
// 設(shè)置存儲(chǔ)key
// 設(shè)置Header的高度
}
- (void)placeSubviews {
[super placeSubviews];
// 設(shè)置Header的位置(y坐標(biāo))
}
2.偏移變化:- scrollViewContentSizeDidChange
當(dāng)用戶拖拽滾動(dòng)控件痹仙,是其偏移值發(fā)生改變時(shí)是尔,會(huì)回調(diào)- scrollViewContentOffsetDidChange:(NSDictionary *)change
方法,在不同的狀態(tài)下執(zhí)行對(duì)應(yīng)的邏輯开仰。如果滾動(dòng)視圖已經(jīng)將Header滾動(dòng)至屏幕外拟枚,則不處理后續(xù)邏輯。
- scrollViewContentOffsetDidChange:(NSDictionary *)change
方法中众弓,有一些關(guān)鍵的變量值恩溅,分別是:
- 當(dāng)前滾動(dòng)的偏移值:offsetY
- 頭部控件剛好出現(xiàn)的偏移值:happenOffsetY
- 即將刷新的臨界點(diǎn):normal2pullingOffsetY
通過(guò)對(duì)這些變量值的比較,可以計(jì)算出拖拽動(dòng)作應(yīng)該被設(shè)置為何種狀態(tài)谓娃。
- 控件正在被拖拽
- 當(dāng)拖拽時(shí)的偏移量大于臨界值脚乡,且原狀態(tài)為閑置時(shí),將狀態(tài)置為即將刷新
- 當(dāng)拖拽時(shí)的偏移量小于臨界值傻粘,且原狀態(tài)為即將刷新時(shí)每窖,將狀態(tài)重置會(huì)閑置
- 控件未被拖拽,且當(dāng)前狀態(tài)為松手進(jìn)行刷新
- 執(zhí)行開(kāi)始刷新的方法
- 控件未被拖拽弦悉,且為達(dá)到執(zhí)行刷新回調(diào)的臨界點(diǎn)
if (self.scrollView.isDragging) { // 如果正在拖拽
self.pullingPercent = pullingPercent;
// 當(dāng)拖拽時(shí)的偏移量大于臨界值窒典,且原狀態(tài)為閑置時(shí),將狀態(tài)置為即將刷新
if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) {
// 轉(zhuǎn)為即將刷新?tīng)顟B(tài)
self.state = MJRefreshStatePulling;
}
// 當(dāng)拖拽時(shí)的偏移量小于臨界值稽莉,且原狀態(tài)為即將刷新時(shí)瀑志,將狀態(tài)重置會(huì)閑置
else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) {
// 轉(zhuǎn)為普通狀態(tài)
self.state = MJRefreshStateIdle;
}
}
// 原狀態(tài)為即將刷新,且手已松開(kāi)
else if (self.state == MJRefreshStatePulling) {
// 開(kāi)始刷新
[self beginRefreshing];
}
// 未達(dá)到刷新的偏移量污秆,且手已松開(kāi)
else if (pullingPercent < 1) {
// 記錄header露出的百分比
self.pullingPercent = pullingPercent;
}
這里需要注意的是劈猪,當(dāng)Header的狀態(tài)處于MJRefreshStateRefreshing
正在刷新,且控件還在滾動(dòng)時(shí)良拼,會(huì)執(zhí)行- resetInset
方法战得,目的是記錄刷新結(jié)束后需要調(diào)整的上邊距值insetTDelta
,同時(shí)避免 CollectionView 在使用根據(jù) Autolayout 和 內(nèi)容自動(dòng)伸縮 Cell, 刷新時(shí)導(dǎo)致的 Layout 異常渲染問(wèn)題庸推。
3.狀態(tài)設(shè)置
- (void)setState:(MJRefreshState)state{
_state = state;
// 加入主隊(duì)列的目的是等setState:方法調(diào)用完畢常侦、設(shè)置完文字后再去布局子控件
MJRefreshDispatchAsyncOnMainQueue([self setNeedsLayout];)
}
視圖刷新被加入了異步隊(duì)列的主線程中,是為了盡量等空間的屬性設(shè)置完畢后再進(jìn)行布局的刷新贬媒。
4.開(kāi)始刷新
執(zhí)行- beginRefreshing
方法聋亡,設(shè)置狀態(tài)為MJRefreshStateRefreshing
刷新中。 方法調(diào)用流程如下:
1.開(kāi)始刷新方法調(diào)用
- (void)beginRefreshing{
// ...
self.state = MJRefreshStateRefreshing;
// ...
}
2.設(shè)置狀態(tài)為正在刷新中
- (void)setState:(MJRefreshState)state{
MJRefreshCheckState
// 根據(jù)狀態(tài)做事情
if (state == MJRefreshStateIdle) {
//...
} else if (state == MJRefreshStateRefreshing) {
[self headerRefreshingAction];
}
}
3.執(zhí)行刷新動(dòng)作
- (void)headerRefreshingAction {
// 主要代碼
[UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
if (self.scrollView.panGestureRecognizer.state != UIGestureRecognizerStateCancelled) {
CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
// 增加滾動(dòng)區(qū)域top
self.scrollView.mj_insetT = top;
// 設(shè)置滾動(dòng)位置
CGPoint offset = self.scrollView.contentOffset;
offset.y = -top;
[self.scrollView setContentOffset:offset animated:NO];
}
} completion:^(BOOL finished) {
[self executeRefreshingCallback];
}];
}
- headerRefreshingAction
方法為滾動(dòng)視圖設(shè)置了新的inset
和offset
际乘,使得Header能在滾動(dòng)視圖的頂部停留坡倔,用于展示刷新文字動(dòng)畫(huà)之類(lèi)的。
5.結(jié)束刷新
結(jié)束刷新需要使用者在耗時(shí)操作結(jié)束后脖含,主動(dòng)調(diào)用- endRefreshing
方法罪塔。 方法調(diào)用流程如下:
1.結(jié)束刷新方法調(diào)用
- (void)endRefreshing{
MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateIdle;)
}
2.設(shè)置狀態(tài)為閑置
- (void)setState:(MJRefreshState)state{
MJRefreshCheckState
// 根據(jù)狀態(tài)做事情
if (state == MJRefreshStateIdle) {
if (oldState != MJRefreshStateRefreshing) return;
[self headerEndingAction];
} else if (state == MJRefreshStateRefreshing) {
// ...
}
}
3.執(zhí)行結(jié)束動(dòng)作
- (void)headerEndingAction {;}
- headerEndingAction
方法將滾動(dòng)視圖的inset
重置為刷新?tīng)顟B(tài)前的值,將header又隱藏了起來(lái)
上拉加載控件(Footer)
下拉刷新控件包含七個(gè)類(lèi):
- MJRefreshFooter
- MJRefreshBackFooter
- MJRefreshBackNormalFooter
- MJRefreshBackGifFooter
- MJRefreshAutoFooter
- MJRefreshAutoNormalFooter
- MJRefreshAutoGifFooter
- MJRefreshBackFooter
MJRefreshFooter
MJRefreshFooter
類(lèi)不能直接被使用器赞,它僅定義了少量的基礎(chǔ)屬性和方法垢袱,例如構(gòu)造方法、初始化控件高度港柜,以及無(wú)數(shù)據(jù)加載情況下的處理请契。
能夠直接使用的上拉加載控件是,由MJRefreshFooter
衍生出的兩個(gè)子類(lèi)夏醉,MJRefreshBackFooter
和MJRefreshAutoFooter
爽锥,這兩個(gè)控件的不同之處在于:
-
MJRefreshBackFooter
:隱藏在滾動(dòng)視圖的底部邊界之外,當(dāng)拖動(dòng)至Footer的刷新臨界點(diǎn)并放開(kāi)手畔柔,才會(huì)執(zhí)行加載操作氯夷。 -
MJRefreshAutoFooter
:會(huì)緊貼在滾動(dòng)視圖contentSize的邊界,如果contentSize的尺寸小于滾動(dòng)視圖的尺寸靶擦,用戶在不需要滾動(dòng)的情況下也能看到Footer控件的腮考。它的刷新時(shí)機(jī)是雇毫,用戶在拖拽中且達(dá)到了Footer刷新臨界點(diǎn)。
MJRefreshBackFooter
實(shí)現(xiàn)過(guò)程
1.初始化
當(dāng)MJRefreshBackFooter即將被加入父視圖時(shí)踩蔚,會(huì)走 - willMoveToSuperview:
方法棚放,并在方法體內(nèi)調(diào)用 - scrollViewContentSizeDidChange:
方法。該方法獲取了父視圖高度和父視圖內(nèi)容的高度馅闽,取二者中較大的數(shù)飘蚯,作為Footer的縱坐標(biāo)值,確保Footer的位置正好隱藏在視圖或內(nèi)容的最底部福也。
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change
{
[super scrollViewContentSizeDidChange:change];
// 內(nèi)容的高度
CGFloat contentHeight = self.scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom;
// 表格的高度
CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom;
// 設(shè)置位置和尺寸
self.mj_y = MAX(contentHeight, scrollHeight);
}
2.偏移變化:- scrollViewContentSizeDidChange
當(dāng)用戶在滑動(dòng)控件使offset發(fā)生變化時(shí)局骤,會(huì)觸發(fā) MJRefreshKeyPathContentOffset
的監(jiān)聽(tīng)事件 —— - scrollViewContentOffsetDidChange
。 MJRefreshBackFooter
在- scrollViewContentOffsetDidChange
方法里的代碼邏輯與MJRefreshHeader
是幾乎相同的暴凑,這里不贅述峦甩。需要提一點(diǎn)的是,MJRefreshBackFooter
視圖的臨界值計(jì)算需要考慮內(nèi)容的高度與滾動(dòng)視圖之間的高度差問(wèn)題搬设。
#pragma mark 獲得scrollView的內(nèi)容 超出 view 的高度
- (CGFloat)heightForContentBreakView
{
CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top;
return self.scrollView.contentSize.height - h;
}
#pragma mark 剛好看到上拉刷新控件時(shí)的contentOffset.y
- (CGFloat)happenOffsetY
{
CGFloat deltaH = [self heightForContentBreakView];
// 內(nèi)容和視圖的高度差
if (deltaH > 0) {
// 內(nèi)容高度 > 視圖高度
return deltaH - self.scrollViewOriginalInset.top;
} else {
// 內(nèi)容高度 < 視圖高度
return - self.scrollViewOriginalInset.top;
}
}
3.狀態(tài)設(shè)置
MJRefreshBackFooter
類(lèi)的- setState
方法的主要工作就是在開(kāi)始刷新和結(jié)束刷新的時(shí)候穴店,為滾動(dòng)視圖更新對(duì)應(yīng)的offset
和inset
值。
MJRefreshAutoFooter
實(shí)現(xiàn)過(guò)程
1.初始化
當(dāng)MJRefreshAutoFooter即將被加入父視圖時(shí)拿穴,會(huì)調(diào)用- willMoveToSuperview:
方法泣洞,該方法獲取了父視圖的內(nèi)容,作為Footer的縱坐標(biāo)y的值默色,確保Footer的位置正好緊貼內(nèi)容的底部球凰。
- (void)willMoveToSuperview:(UIView *)newSuperview
{
[super willMoveToSuperview:newSuperview];
if (newSuperview) { // 新的父控件
if (self.hidden == NO) {
self.scrollView.mj_insetB += self.mj_h;
}
// 設(shè)置位置
self.mj_y = _scrollView.mj_contentH;
} else { // 被移除了
if (self.hidden == NO) {
self.scrollView.mj_insetB -= self.mj_h;
}
}
}
2.刷新邏輯
MJRefreshAutoFooter
控件的位置是緊貼內(nèi)容的,所以會(huì)存在兩種情況: 1.當(dāng)內(nèi)容高度 < 控件高度腿宰,可以直接看到緊貼內(nèi)容底部的Footer呕诉。這種情況下,加載的時(shí)機(jī)是在用戶松手后調(diào)用的吃度。
- (void)scrollViewPanStateDidChange:(NSDictionary *)change
{
[super scrollViewPanStateDidChange:change];
if (self.state != MJRefreshStateIdle) return;
UIGestureRecognizerState panState = _scrollView.panGestureRecognizer.state;
switch (panState) {
// 手松開(kāi)
case UIGestureRecognizerStateEnded: {
if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) {
// 內(nèi)容 < 控件高度
if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽
self.triggerByDrag = YES;
[self beginRefreshing];
}
} else {
// 內(nèi)容 > 控件高度
if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) {
self.triggerByDrag = YES;
[self beginRefreshing];
}
}
}
break;
case UIGestureRecognizerStateBegan: {
[self resetTriggerTimes];
}
break;
default:
break;
}
}
2.當(dāng)內(nèi)容高度 ≥ 控件高度甩挫,需要拖動(dòng)視圖到Footer的加載臨界值,但此時(shí)不需要松開(kāi)手椿每,只要滾動(dòng)視圖的偏移量突破了臨界值伊者,就會(huì)觸發(fā)加載方法。
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
{
[super scrollViewContentOffsetDidChange:change];
if (self.state != MJRefreshStateIdle || !self.automaticallyRefresh || self.mj_y == 0) return;
// 當(dāng)autoTriggerTimes被設(shè)置成-1(滾動(dòng)時(shí)無(wú)限加載)
// 該方法保證拖動(dòng)放手后间护,視圖還在滾動(dòng)的情況下亦渗,一直保持加載狀態(tài)
// 內(nèi)容超出控件高度
if (_scrollView.mj_insetT + _scrollView.mj_contentH > _scrollView.mj_h) {
// 內(nèi)容高度 - 控件高度 + 控件底部邊距 + footer高度 * 百分比 - footer高度
// 內(nèi)容高度 - 控件高度 + 控件底部邊距
if (_scrollView.mj_offsetY >= _scrollView.mj_contentH - _scrollView.mj_h + self.mj_h * self.triggerAutomaticallyRefreshPercent + _scrollView.mj_insetB - self.mj_h) {
// 防止手松開(kāi)時(shí)連續(xù)調(diào)用
CGPoint old = [change[@"old"] CGPointValue];
CGPoint new = [change[@"new"] CGPointValue];
if (new.y <= old.y) return;
if (_scrollView.isDragging) {
self.triggerByDrag = YES;
}
// 當(dāng)?shù)撞克⑿驴丶耆霈F(xiàn)時(shí),才刷新
[self beginRefreshing];
}
}
}
從代碼中可以看出汁尺,滿足刷新的條件是: 拖動(dòng)偏移量 ≥ 內(nèi)容高度 - 控件高度 + 控件底部邊距 + footer高度 * 刷新控件露出百分比 - footer高度
當(dāng)刷新控件露出百分比為默認(rèn)值1.時(shí)法精,不等式可以簡(jiǎn)化為: 拖動(dòng)偏移量 ≥ 內(nèi)容高度 - 控件高度 + 控件底部邊距
3.無(wú)限觸發(fā)
MJRefreshAutoFooter
的另一特點(diǎn)就是無(wú)限觸發(fā),開(kāi)發(fā)者可以設(shè)置屬性自定義自動(dòng)刷新的次數(shù)。
/** 自動(dòng)觸發(fā)次數(shù), 默認(rèn)為 1, 僅在拖拽 ScrollView 時(shí)才生效,
如果為 -1, 則為無(wú)限觸發(fā)
*/
@property (nonatomic) NSInteger autoTriggerTimes;
當(dāng)滾動(dòng)視圖在持續(xù)地滾動(dòng)時(shí)(內(nèi)容高度≥控件高度)搂蜓,會(huì)不停地調(diào)用-scrollViewContentOffsetDidChange:
方法狼荞,滿足加載條件時(shí)從而不停的調(diào)用-beginRefreshing
方法。
- (void)beginRefreshing{
// 新的拖拽動(dòng)作 && 剩余觸發(fā)次數(shù) && 是否無(wú)限觸發(fā)
if (self.triggerByDrag && self.leftTriggerTimes <= 0 && !self.unlimitedTrigger) {
return;
}
[super beginRefreshing];
}
當(dāng)前如果支持無(wú)限觸發(fā)autoTriggerTimes == -1
帮碰,那么在滾動(dòng)視圖停止?jié)L動(dòng)前粘秆,視圖到達(dá)加載臨界點(diǎn)時(shí)都會(huì)觸發(fā)加載任務(wù)。
4.狀態(tài)設(shè)置
-beginRefreshing
在觸發(fā)的時(shí)候收毫,會(huì)將state
設(shè)置成MJRefreshStateRefreshing
,并執(zhí)行加載數(shù)據(jù)的回調(diào)殷勘。 通常我們會(huì)在加載數(shù)據(jù)結(jié)束的回調(diào)方法中去調(diào)用-endRefreshing
或-endRefreshingWithNoMoreData
方法此再,此時(shí)state
會(huì)被設(shè)置成MJRefreshStateIdle
或MJRefreshStateNoMoreData
,對(duì)應(yīng)-setState:
方法中的代碼玲销,我們可以看到無(wú)限觸發(fā)次數(shù)是在此處進(jìn)行了控制输拇。
- (void)setState:(MJRefreshState)state{
MJRefreshCheckState
if (state == MJRefreshStateRefreshing) {
// 執(zhí)行加載數(shù)據(jù)回調(diào)
[self executeRefreshingCallback];
} else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
if (self.triggerByDrag) {
if (!self.unlimitedTrigger) {
self.leftTriggerTimes -= 1;
}
self.triggerByDrag = NO;
}
/** 結(jié)束刷新 */
if (MJRefreshStateRefreshing == oldState) {
// 當(dāng)視圖開(kāi)啟了分頁(yè)顯示,設(shè)置動(dòng)畫(huà)和回調(diào)
if (self.scrollView.pagingEnabled) {
CGPoint offset = self.scrollView.contentOffset;
offset.y -= self.scrollView.mj_insetB;
[UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
self.scrollView.contentOffset = offset;
if (self.endRefreshingAnimationBeginAction) {
self.endRefreshingAnimationBeginAction();
}
} completion:^(BOOL finished) {
if (self.endRefreshingCompletionBlock) {
self.endRefreshingCompletionBlock();
}
}];
return;
}
// 結(jié)束刷新回調(diào)
if (self.endRefreshingCompletionBlock) {
self.endRefreshingCompletionBlock();
}
}
}
}
左滑加載控件(Trailer)
MJRefreshTrailer
在實(shí)現(xiàn)邏輯上與 MJRefreshBackFooter
是完全一樣的贤斜,只不過(guò)是將部分參數(shù)從垂直方向換成了水平方向策吠。
State、Normal 子類(lèi)控件
State類(lèi)型的控件 的主要特點(diǎn)是添加了不同狀態(tài)的提示文字和刷新時(shí)間的顯示瘩绒。 Normal類(lèi)型的控件 在 State類(lèi)型控件 的基礎(chǔ)上猴抹,添加了箭頭圖標(biāo)和刷新的動(dòng)畫(huà)。
Gif 子類(lèi)控件
Gif類(lèi)型的控件可以在拖拽和刷新時(shí)展示精美的動(dòng)畫(huà)來(lái)提升用戶體驗(yàn)锁荔。 主要的兩個(gè)方法是:
- (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state
{
if (images == nil) return;
self.stateImages[@(state)] = images;
self.stateDurations[@(state)] = @(duration);
/* 根據(jù)圖片設(shè)置控件的高度 */
UIImage *image = [images firstObject];
if (image.size.height > self.mj_h) {
self.mj_h = image.size.height;
}
}
- (void)setImages:(NSArray *)images forState:(MJRefreshState)state {
[self setImages:images duration:images.count * 0.1 forState:state];
}
刷新控件會(huì)根據(jù)圖片的高度調(diào)整自身高度蟀给,同時(shí)會(huì)在沒(méi)有自定義動(dòng)畫(huà)時(shí)長(zhǎng)的情況下,根據(jù)動(dòng)畫(huà)的幀數(shù)自動(dòng)設(shè)置完整播放一遍動(dòng)畫(huà)的時(shí)間阳堕。
拖拽動(dòng)畫(huà)
開(kāi)發(fā)者可以通過(guò)拖拽百分比設(shè)置用戶在拖拽時(shí)的動(dòng)畫(huà)跋理,具體實(shí)現(xiàn)方式是通過(guò)計(jì)算當(dāng)前拖拽的百分比在整體動(dòng)畫(huà)中對(duì)應(yīng)的某個(gè)幀的圖片來(lái)獲取大致的下標(biāo)。
// 通過(guò)拖拽百分比設(shè)置 Idle~Pulling狀態(tài)之間的 對(duì)應(yīng)的動(dòng)畫(huà)幀
- (void)setPullingPercent:(CGFloat)pullingPercent
{
[super setPullingPercent:pullingPercent];
NSArray *images = self.stateImages[@(MJRefreshStateIdle)];
if (self.state != MJRefreshStateIdle || images.count == 0) return;
// 停止動(dòng)畫(huà)
[self.gifView stopAnimating];
// 設(shè)置當(dāng)前需要顯示的圖片
NSUInteger index = images.count * pullingPercent;
if (index >= images.count) index = images.count - 1;
self.gifView.image = images[index];
}
刷新動(dòng)畫(huà)
刷新動(dòng)畫(huà)會(huì)在視圖狀態(tài)處于“即將開(kāi)始刷新”和“刷新中”進(jìn)行恬总,使用UIImageView的startAnimating
對(duì)提前設(shè)置好的圖片組逐幀播放前普,默認(rèn)情況下是無(wú)限循環(huán)播放的。
- (void)setState:(MJRefreshState)state
{
MJRefreshCheckState
// 根據(jù)狀態(tài)做事情
if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) {
// 即將刷新 和 刷新中 狀態(tài)的動(dòng)畫(huà)
NSArray *images = self.stateImages[@(state)];
if (images.count == 0) return;
[self.gifView stopAnimating];
if (images.count == 1) { // 單張圖片
self.gifView.image = [images lastObject];
} else { // 多張圖片
self.gifView.animationImages = images;
self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
[self.gifView startAnimating];
}
} else if (state == MJRefreshStateIdle) {
// 限制狀態(tài)停止動(dòng)畫(huà)
[self.gifView stopAnimating];
}
}
總結(jié)
MJRefresh清晰整齊的架構(gòu)為開(kāi)發(fā)者提供了及其豐富的擴(kuò)展性壹堰,而在通常沒(méi)有定制需求的情況下拭卿,默認(rèn)的控件已經(jīng)十分夠用了。
以下資料在群文件可自行下載
文章到這里就結(jié)束了缀旁,你也可以私信我及時(shí)獲取最新資料以及面試相關(guān)資料记劈。如果你有什么意見(jiàn)和建議歡迎給我留言。