MJRefresh閱讀筆記

李明杰老師的代表作之一MJRefresh可以說(shuō)是棒棒的,很多小伙伴都會(huì)在沒(méi)有什么特殊要求的情況下使用這個(gè)框架滔韵,簡(jiǎn)單好用,功能還豐富,我也是用了很久械巡,最近項(xiàng)目不是很忙,決定研究一下老師的設(shè)計(jì)思路库菲。

這個(gè)框架第一遍看的時(shí)候我是懵逼的琐鲁,一句代碼要跳轉(zhuǎn)很多類,看的暈頭轉(zhuǎn)向惭每,造成這個(gè)原因主要是因?yàn)槔罾蠋煹目蚣茉O(shè)計(jì)采用繼承的方式骨饿,有一個(gè)基類MJRefreshComponent,然后所以類都是繼承于它台腥,每個(gè)子類實(shí)現(xiàn)不同的功能宏赘,如此一層層繼承下去,這樣的做的好處方便擴(kuò)展黎侈,如果你想要自定義某些東西的時(shí)候察署,可以選擇一個(gè)子類繼承,然后在原有的基礎(chǔ)上加?xùn)|西峻汉,很方便贴汪。什么意思呢,比方說(shuō)A類實(shí)現(xiàn)的a功能休吠,B類繼承于A類扳埂,它實(shí)現(xiàn)了b功能,C類繼承于B類瘤礁,實(shí)現(xiàn)了c功能阳懂,那么現(xiàn)在創(chuàng)建一個(gè)D類,繼承于C類蔚携,那么這個(gè)D類同時(shí)擁有的a,b,c三個(gè)功能希太,如有你不想擁有c功能,這個(gè)時(shí)候你可以讓D類繼承于B類酝蜒,那么D類就只擁有a和b兩個(gè)功能誊辉,在此基礎(chǔ)上你可以自己再添加e,f,g等等功能,這樣做方便擴(kuò)展亡脑。我是這么理解的堕澄,如果有不對(duì)的地方邀跃,歡迎大家指正,共同學(xué)習(xí)蛙紫。那么下面進(jìn)入正題了拍屑。


這張圖是在其他文章里看到,清楚的介紹了這個(gè)框架的結(jié)構(gòu)坑傅,下面我們具體看看都做了啥僵驰。

基類MJRefreshComponent繼承與UIView,它主要做了哪么事呢唁毒?

1.定義了刷新的所有狀態(tài)蒜茴,一共6種

typedef NS_ENUM(NSInteger, MJRefreshState) {
??? /** 普通閑置狀態(tài) */
??? MJRefreshStateIdle = 1,
??? /** 松開就可以進(jìn)行刷新的狀態(tài) */
??? MJRefreshStatePulling,
??? /** 正在刷新中的狀態(tài) */
??? MJRefreshStateRefreshing,
??? /** 即將刷新的狀態(tài) */
??? MJRefreshStateWillRefresh,
??? /** 所有數(shù)據(jù)加載完畢,沒(méi)有更多的數(shù)據(jù)了 */
??? MJRefreshStateNoMoreData
}

2.定義了一些回調(diào)

/** 進(jìn)入刷新狀態(tài)的回調(diào) */
typedef void (^MJRefreshComponentRefreshingBlock)(void);
/** 開始刷新后的回調(diào)(進(jìn)入刷新狀態(tài)后的回調(diào)) */
typedef void (^MJRefreshComponentbeginRefreshingCompletionBlock)(void);
/** 結(jié)束刷新后的回調(diào) */
typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void);

3.初始化方法

- (instancetype)initWithFrame:(CGRect)frame

{

? ? if (self = [super initWithFrame:frame]) {

? ? ? ? // 準(zhǔn)備工作

? ? ? ? [self prepare];

???????? // 默認(rèn)是普通狀態(tài)

? ? ? ? self.state = MJRefreshStateIdle;

? ? }

? ? return self;

}

4.添加了對(duì)scrollView屬性的監(jiān)聽浆西,刷新功能主要是利用scrollView的三個(gè)屬性來(lái)實(shí)現(xiàn)的粉私, contentOffset:scrollView的偏移量,contentSize:內(nèi)容大小近零,contentInset:內(nèi)容距離上下左右的距離诺核,正在刷新的時(shí)候刷新控件會(huì)在頂部停留,刷新之后刷新控件會(huì)上移消失久信,這個(gè)效果就是通過(guò)設(shè)置contentInset的top來(lái)實(shí)現(xiàn)的窖杀,后面會(huì)具體說(shuō)道,如果對(duì)這三個(gè)屬性的用法不太了解入篮,最好先去寫個(gè)scorllView研究一下這三個(gè)屬性具體干嘛的陈瘦,不然后面的介紹可能會(huì)比較不好理解。

- (void)addObservers

{

? ? NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;

? ? [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil];

? ? [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil];

? ? self.pan = self.scrollView.panGestureRecognizer;

? ? [self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil];

}

5.留給子類實(shí)現(xiàn)的接口

- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}
- (void)scrollViewPanStateDidChange:(NSDictionary *)change{}

6.所有子類都實(shí)現(xiàn)的一個(gè)方法潮售,所有子類根據(jù)不同的狀態(tài)做出不同的響應(yīng)都是通過(guò)- (void)setState:(MJRefreshState)state這個(gè)方法來(lái)實(shí)現(xiàn)的

- (void)setState:(MJRefreshState)state

{ _state = state;

// 加入主隊(duì)列的目的是等setState:方法調(diào)用完畢、設(shè)置完文字后再去布局子控件

dispatch_async(dispatch_get_main_queue(), ^{

? ? ? ? [self setNeedsLayout];

? ? });

}我們看一下這個(gè)方法里面就幾行代碼锅风,第一句_state = state;就是簡(jiǎn)單的賦值酥诽,然后是一個(gè)GCD知識(shí),開啟一個(gè)異步任務(wù)皱埠,將block添加到主隊(duì)列里肮帐,異步任務(wù)的特點(diǎn)就是不用等到它執(zhí)行完就可以執(zhí)行下面的任務(wù),所有代碼會(huì)繼續(xù)往下走边器,之后才執(zhí)行 [self setNeedsLayout];這句代碼训枢,這樣的做的目的是等setState:方法調(diào)用完畢、設(shè)置完文字后再去布局子控件忘巧。

7.提供了開始很暫停刷新的接口

- (void)beginRefreshing;
- (void)endRefreshing;

MJRefreshComponent這個(gè)類下面分出來(lái)兩條線MJRefreshHeader和MJRefreshFooter恒界,顧名思義一個(gè)是下來(lái)刷新,一個(gè)是上拉加載對(duì)應(yīng)的head和foot砚嘴。我們看一下MJRefreshHeader這個(gè)類都干嘛了十酣,MJRefreshFooter這個(gè)類對(duì)比著學(xué)習(xí)就行了涩拙。

MJRefreshHeade主要做了那些事?

1.設(shè)置了初始化方法

+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock
{
??? MJRefreshHeader *cmp = [[self alloc] init];
??? //cmp.backgroundColor = [UIColor blueColor];
??? cmp.refreshingBlock = refreshingBlock;
??? return cmp;
}
+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action
{
??? MJRefreshHeader *cmp = [[self alloc] init];
??? [cmp setRefreshingTarget:target refreshingAction:action];
??? return cmp;
}

2.設(shè)置了head的高度

- (void)placeSubviews

{[super placeSubviews];// 設(shè)置y值(當(dāng)自己的高度發(fā)生改變了耸采,肯定要重新調(diào)整Y值兴泥,所以放到placeSubviews方法中設(shè)置y值)

? ? self.mj_y = - self.mj_h - self.ignoredScrollViewContentInsetTop;

}

3.滑動(dòng)的回調(diào)方法- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change,只要偏移量發(fā)生變化就會(huì)掉這個(gè)方法虾宇,這個(gè)方法的綁定是在父類的kvo里面搓彻。具體代碼邏輯

- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change

{

? ? [super scrollViewContentOffsetDidChange:change];

// 在刷新的refreshing狀態(tài)

? ? if (self.state == MJRefreshStateRefreshing) {

? ? ? ? // 暫時(shí)保留

? ? ? ? if (self.window == nil) return;

???? // sectionheader停留解決

? ? ? ? CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top;

? ? ? ? insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT;

? ? ? ? self.scrollView.mj_insetT = insetT;

??????? self.insetTDelta = _scrollViewOriginalInset.top - insetT;

? ? ? ? return;

? ? }

// 跳轉(zhuǎn)到下一個(gè)控制器時(shí),contentInset可能會(huì)變

? ? _scrollViewOriginalInset = self.scrollView.mj_inset;

?// 當(dāng)前的contentOffset

? ? CGFloat offsetY = self.scrollView.mj_offsetY;

? ? // 頭部控件剛好出現(xiàn)的offsetY

? ? CGFloat happenOffsetY = - self.scrollViewOriginalInset.top;

?? // 如果是向上滾動(dòng)到看不見(jiàn)頭部控件嘱朽,直接返回

? ? // >= -> >

? ? if (offsetY > happenOffsetY) return;


? ? // 普通 和 即將刷新 的臨界點(diǎn)

? ? CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_h;

? ? CGFloat pullingPercent = (happenOffsetY - offsetY) / self.mj_h;


? ? if (self.scrollView.isDragging) { // 如果正在拖拽

? ? ? ? self.pullingPercent = pullingPercent;

? ? ? ? if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) {

? ? ? ? ? ? // 轉(zhuǎn)為即將刷新狀態(tài)

? ? ? ? ? ? self.state = MJRefreshStatePulling;

? ? ? ? } else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) {

? ? ? ? ? ? // 轉(zhuǎn)為普通狀態(tài)

? ? ? ? ? ? self.state = MJRefreshStateIdle;

? ? ? ? }

? ? } else if (self.state == MJRefreshStatePulling) {// 即將刷新 && 手松開

? ? ? ? // 開始刷新

? ? ? ? [self beginRefreshing];

? ? } else if (pullingPercent < 1) {

? ? ? ? self.pullingPercent = pullingPercent;

? ? }

}

4.根據(jù)不同的狀態(tài)設(shè)置不同的效果還是調(diào)用- (void)setState:(MJRefreshState)state方法

- (void)setState:(MJRefreshState)state

{

? ? MJRefreshCheckState


? ? // 根據(jù)狀態(tài)做事情

? ? if (state == MJRefreshStateIdle) {

? ? ? ? if (oldState != MJRefreshStateRefreshing) return;


? ? ? ? // 保存刷新時(shí)間

? ? ? ? [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey];

? ? ? ? [[NSUserDefaults standardUserDefaults] synchronize];


? ? ? ? // 恢復(fù)inset和offset

? ? ? ? [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{

? ? ? ? ? ? self.scrollView.mj_insetT += self.insetTDelta;// 自動(dòng)調(diào)整透明度

? ? ? ? ? ? if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;

? ? ? ? } completion:^(BOOL finished) {

? ? ? ? ? ? self.pullingPercent = 0.0;


? ? ? ? ? ? if (self.endRefreshingCompletionBlock) {

? ? ? ? ? ? ? ? self.endRefreshingCompletionBlock();

? ? ? ? ? ? }

? ? ? ? }];

? ? } else if (state == MJRefreshStateRefreshing) {

? ? ? ? dispatch_async(dispatch_get_main_queue(), ^{

? ? ? ? ? ? [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{

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

? ? ? ? ? ? }];

? ? ? ? });

? ? }

}

接下來(lái)就到了MJRefreshStateHeader這個(gè)類

MJRefreshStateHeader繼承于MJRefreshHeader旭贬,它的作用是給刷新控件添加了兩個(gè)label,一個(gè)顯示刷新狀態(tài)燥翅,一個(gè)顯示上次刷新時(shí)間骑篙,具體看一下里面的代碼。

1.首先將所有狀態(tài)對(duì)應(yīng)的文字添加到字典stateTitles中森书,這樣做的目的是根據(jù)不同的狀態(tài)取出對(duì)應(yīng)的文字內(nèi)容靶端。

? ? // 初始化文字

? ? [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderIdleText] forState:MJRefreshStateIdle];

? ? [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderPullingText] forState:MJRefreshStatePulling];

? ? [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderRefreshingText] forState:MJRefreshStateRefreshing];

- (void)setTitle:(NSString *)title forState:(MJRefreshState)state

{

? ? if (title == nil) return;

? ? self.stateTitles[@(state)] = title;

? ? self.stateLabel.text = self.stateTitles[@(self.state)];

}

2.關(guān)于更新時(shí)間的格式處理

- (void)setLastUpdatedTimeKey:(NSString *)lastUpdatedTimeKey

{

? ? [super setLastUpdatedTimeKey:lastUpdatedTimeKey];


? ? // 如果label隱藏了,就不用再處理

? ? if (self.lastUpdatedTimeLabel.hidden) return;


? ? NSDate *lastUpdatedTime = [[NSUserDefaults standardUserDefaults] objectForKey:lastUpdatedTimeKey];


? ? // 如果有block

? ? if (self.lastUpdatedTimeText) {

? ? ? ? self.lastUpdatedTimeLabel.text = self.lastUpdatedTimeText(lastUpdatedTime);

? ? ? ? return;

? ? }


? ? if (lastUpdatedTime) {

? ? ? ? // 1.獲得年月日

? ? ? ? NSCalendar *calendar = [self currentCalendar];

? ? ? ? NSUInteger unitFlags = NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay |NSCalendarUnitHour |NSCalendarUnitMinute;

? ? ? ? NSDateComponents *cmp1 = [calendar components:unitFlags fromDate:lastUpdatedTime];

? ? ? ? NSDateComponents *cmp2 = [calendar components:unitFlags fromDate:[NSDate date]];


? ? ? ? // 2.格式化日期

? ? ? ? NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

? ? ? ? BOOL isToday = NO;

? ? ? ? if ([cmp1 day] == [cmp2 day]) { // 今天

? ? ? ? ? ? formatter.dateFormat = @" HH:mm";

? ? ? ? ? ? isToday = YES;

? ? ? ? } else if ([cmp1 year] == [cmp2 year]) { // 今年

? ? ? ? ? ? formatter.dateFormat = @"MM-dd HH:mm";

? ? ? ? } else {

? ? ? ? ? ? formatter.dateFormat = @"yyyy-MM-dd HH:mm";

? ? ? ? }

? ? ? ? NSString *time = [formatter stringFromDate:lastUpdatedTime];


? ? ? ? // 3.顯示日期

? ? ? ? self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@%@",

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [NSBundle mj_localizedStringForKey:MJRefreshHeaderLastTimeText],

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? isToday ? [NSBundle mj_localizedStringForKey:MJRefreshHeaderDateTodayText] : @"",

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? time];

? ? } else {

? ? ? ? self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@",

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [NSBundle mj_localizedStringForKey:MJRefreshHeaderLastTimeText],

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [NSBundle mj_localizedStringForKey:MJRefreshHeaderNoneLastDateText]];

? ? }


}這里面有一段代碼if (self.lastUpdatedTimeText) {

? ? ? ? self.lastUpdatedTimeLabel.text = self.lastUpdatedTimeText(lastUpdatedTime);

? ? ? ? return;

? ? }凛膏,這段代碼的作用是讓用戶可以自定義事件格式杨名,如果你不想用它的。


最后就是MJRefreshNormalHeader這個(gè)類了

MJRefreshNormalHeader繼承于MJRefreshStateHeader猖毫,它主要是給刷新視圖添加了箭頭指示和菊花台谍。

1.布局箭頭和菊花

- (void)placeSubviews

{

? ? [super placeSubviews];


? ? // 箭頭的中心點(diǎn)

? ? CGFloat arrowCenterX = self.mj_w * 0.5;

? ? if (!self.stateLabel.hidden) {

? ? ? ? CGFloat stateWidth = self.stateLabel.mj_textWith;

? ? ? ? CGFloat timeWidth = 0.0;

? ? ? ? if (!self.lastUpdatedTimeLabel.hidden) {

? ? ? ? ? ? timeWidth = self.lastUpdatedTimeLabel.mj_textWith;

? ? ? ? }

? ? ? ? CGFloat textWidth = MAX(stateWidth, timeWidth);

? ? ? ? arrowCenterX -= textWidth / 2 + self.labelLeftInset;

? ? }

? ? CGFloat arrowCenterY = self.mj_h * 0.5;

? ? CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY);


? ? // 箭頭

? ? if (self.arrowView.constraints.count == 0) {

? ? ? ? self.arrowView.mj_size = self.arrowView.image.size;

? ? ? ? self.arrowView.center = arrowCenter;

? ? }


? ? // 圈圈

? ? if (self.loadingView.constraints.count == 0) {

? ? ? ? self.loadingView.center = arrowCenter;

? ? }

? self.arrowView.tintColor = self.stateLabel.textColor;

?}

2.根據(jù)不同的狀態(tài)顯示不同的效果

- (void)setState:(MJRefreshState)state

{

? ? MJRefreshCheckState


? ? // 根據(jù)狀態(tài)做事情

? ? if (state == MJRefreshStateIdle) {

? ? ? ? if (oldState == MJRefreshStateRefreshing) {

? ? ? ? ? ? self.arrowView.transform = CGAffineTransformIdentity;


? ? ? ? ? ? [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{

? ? ? ? ? ? ? ? self.loadingView.alpha = 0.0;

? ? ? ? ? ? } completion:^(BOOL finished) {

? ? ? ? ? ? ? ? // 如果執(zhí)行完動(dòng)畫發(fā)現(xiàn)不是idle狀態(tài),就直接返回吁断,進(jìn)入其他狀態(tài)

? ? ? ? ? ? ? ? if (self.state != MJRefreshStateIdle) return;


? ? ? ? ? ? ? ? self.loadingView.alpha = 1.0;

? ? ? ? ? ? ? ? [self.loadingView stopAnimating];

? ? ? ? ? ? ? ? self.arrowView.hidden = NO;

? ? ? ? ? ? }];

? ? ? ? } else {

? ? ? ? ? ? [self.loadingView stopAnimating];

? ? ? ? ? ? self.arrowView.hidden = NO;

? ? ? ? ? ? [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{

? ? ? ? ? ? ? ? self.arrowView.transform = CGAffineTransformIdentity;

? ? ? ? ? ? }];

? ? ? ? }

? ? } else if (state == MJRefreshStatePulling) {

? ? ? ? [self.loadingView stopAnimating];

? ? ? ? self.arrowView.hidden = NO;

? ? ? ? [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{

? ? ? ? ? ? self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);

? ? ? ? }];

? ? } else if (state == MJRefreshStateRefreshing) {

? ? ? ? self.loadingView.alpha = 1.0; // 防止refreshing -> idle的動(dòng)畫完畢動(dòng)作沒(méi)有被執(zhí)行

? ? ? ? [self.loadingView startAnimating];

? ? ? ? self.arrowView.hidden = YES;

? ? }

}

這就是整個(gè)下拉刷新大致的流程

MJRefreshComponent------>>>>MJRefreshHeader----->>>>>MJRefreshStateHeader----->>>>MJRefreshNormalHeader

通過(guò)繼承趁蕊,每個(gè)子類實(shí)現(xiàn)各自的功能,從而完成一個(gè)完整地刷新操作仔役。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末掷伙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子又兵,更是在濱河造成了極大的恐慌任柜,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,331評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沛厨,死亡現(xiàn)場(chǎng)離奇詭異宙地,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)逆皮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門宅粥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人页屠,你說(shuō)我怎么就攤上這事粹胯”腿幔” “怎么了?”我有些...
    開封第一講書人閱讀 167,755評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵风纠,是天一觀的道長(zhǎng)况鸣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)竹观,這世上最難降的妖魔是什么镐捧? 我笑而不...
    開封第一講書人閱讀 59,528評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮臭增,結(jié)果婚禮上懂酱,老公的妹妹穿的比我還像新娘。我一直安慰自己誊抛,他們只是感情好列牺,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拗窃,像睡著了一般瞎领。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上随夸,一...
    開封第一講書人閱讀 52,166評(píng)論 1 308
  • 那天九默,我揣著相機(jī)與錄音,去河邊找鬼宾毒。 笑死驼修,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诈铛。 我是一名探鬼主播乙各,決...
    沈念sama閱讀 40,768評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼幢竹!你這毒婦竟也來(lái)了觅丰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,664評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤妨退,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蜕企,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咬荷,經(jīng)...
    沈念sama閱讀 46,205評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評(píng)論 3 340
  • 正文 我和宋清朗相戀三年轻掩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幸乒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,435評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡唇牧,死狀恐怖罕扎,靈堂內(nèi)的尸體忽然破棺而出聚唐,到底是詐尸還是另有隱情,我是刑警寧澤腔召,帶...
    沈念sama閱讀 36,126評(píng)論 5 349
  • 正文 年R本政府宣布杆查,位于F島的核電站,受9級(jí)特大地震影響臀蛛,放射性物質(zhì)發(fā)生泄漏亲桦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評(píng)論 3 333
  • 文/蒙蒙 一浊仆、第九天 我趴在偏房一處隱蔽的房頂上張望客峭。 院中可真熱鬧,春花似錦抡柿、人聲如沸舔琅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)备蚓。三九已至,卻和暖如春闪檬,著一層夾襖步出監(jiān)牢的瞬間星著,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工粗悯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虚循,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,818評(píng)論 3 376
  • 正文 我出身青樓样傍,卻偏偏與公主長(zhǎng)得像横缔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子衫哥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評(píng)論 2 359

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

  • MJRefresh是李明杰老師的作品茎刚,到現(xiàn)在已經(jīng)有9800多顆star了,是一個(gè)簡(jiǎn)單實(shí)用撤逢,功能強(qiáng)大的iOS下拉刷新...
    Style_mao閱讀 661評(píng)論 1 2
  • 本文轉(zhuǎn)載自J_Knight 的MJRefresh源碼解析 MJRefresh是李明杰的作品膛锭,到現(xiàn)在已經(jīng)有9800多...
    Detective41閱讀 660評(píng)論 0 1
  • 正如源碼中注釋的一樣,這個(gè)類的作用是:負(fù)責(zé)監(jiān)控用戶下拉的狀態(tài)蚊荣; pragma mark - 一初狰、在.h文件中,提...
    shareMind閱讀 9,565評(píng)論 0 50
  • 可改進(jìn)部分 在 MJRefreshComponent.h 的 34 行, typedef void (^MJRef...
    在夢(mèng)里失眠閱讀 514評(píng)論 0 0
  • MJRefresh是流行的下拉刷新控件互例,前段時(shí)間為了修復(fù)一個(gè)BUG奢入,讀了它的源碼,本文總結(jié)一下實(shí)現(xiàn)的原理 下拉刷新...
    晚安的你我閱讀 445評(píng)論 0 0