概述
自
2019年初--至今
枝哄,筆者為求生計(jì)肄梨,被迫轉(zhuǎn)學(xué)Vue
開發(fā),老兵不死挠锥,只會(huì)逐漸凋零众羡,以致于漸漸冷落了iOS開發(fā)
,畢竟有舍便有得
蓖租,不逼自己一把粱侣,也不知道自己有多優(yōu)秀羊壹。由于大家對(duì) WeChat 中運(yùn)用的
MVVM + RAC + ViewModel-Based Navigation
的模式比較感興趣,但是此前此項(xiàng)目主要是用于團(tuán)隊(duì)內(nèi)部交流使用齐婴,主要介紹了其中使用技巧和實(shí)用技術(shù)油猫,以及一些細(xì)節(jié)處理,實(shí)用為主柠偶,功能為輔情妖。盡管實(shí)現(xiàn)了微信的整體架構(gòu),以及朋友圈等功能诱担,但是其中還是充斥著不少測(cè)試代碼毡证,這讓整體項(xiàng)目看起來像個(gè)
Demo
,并且不夠優(yōu)美蔫仙,隨著微信 7.0.0+
的出現(xiàn)料睛,整體UI也發(fā)生了翻天覆地的變化,所以摇邦,只好痛定思痛恤煞,重蹈覆轍,重拾iOS涎嚼,這里先以高仿微信通訊錄
為例阱州,宣告筆者強(qiáng)勢(shì)復(fù)出,后期爭(zhēng)取盡自己最大努力法梯,98%還原真實(shí)微信開發(fā)苔货,不斷剖析其中的技術(shù)實(shí)現(xiàn)和細(xì)節(jié)處理。筆者希望通過學(xué)習(xí)和實(shí)踐這個(gè)項(xiàng)目立哑,也能夠打開學(xué)習(xí)
ReactiveCocoa + MVVM
的大門夜惭。當(dāng)然同時(shí)也是拋磚引玉,擺渡眾生铛绰、取長補(bǔ)短诈茧,希望能夠提供一點(diǎn)思路,少走一些彎路捂掰,填補(bǔ)一些細(xì)坑敢会,在幫助他人的過程中,收獲分享技術(shù)的樂趣这嚣。源碼地址:WeChat
預(yù)覽
索引 |
側(cè)滑 |
---|---|
ios_contacts_page_0.png
|
ios_contacts_page_1.png
|
GIF
功能
通訊錄模塊鸥昏,盡管UI看起來極其簡(jiǎn)單,但是涵蓋不少知識(shí)點(diǎn)姐帚,也是通訊錄模塊的功能所在吏垮,本篇文章將詳述以下知識(shí)點(diǎn)以及實(shí)現(xiàn)的細(xì)節(jié):
-
漢字轉(zhuǎn)拼音
、數(shù)據(jù)排序
、按字母分組
底部上拉顯示白底
-
A-Z 索引Bar
膳汪、索引聯(lián)動(dòng)
唯蝶、懸停HeaderView漸變
-
Cell 側(cè)滑備注
,修改側(cè)滑樣式
分析
數(shù)據(jù)處理
首先遗嗽,主要是將聯(lián)系人姓名轉(zhuǎn)成拼音粘我,然后取聯(lián)系人拼音首字母;其次媳谁,利用字典(NSDictionary
)的key
的唯一性涂滴,將聯(lián)系人的首字母插入到字典當(dāng)中去;最后晴音,取出字典的allKeys
進(jìn)行字母排序柔纵,然后遍歷數(shù)據(jù),進(jìn)行按字母分組锤躁。
這里的核心技術(shù)就是漢字轉(zhuǎn)拼音
搁料,當(dāng)然大家可以使用iOS原生庫方法
和 PinYin4Objc來實(shí)現(xiàn),這里筆者主要講講系羞,iOS原生提供的API:
/// string 要轉(zhuǎn)換的string郭计,比如要轉(zhuǎn)換的中文,同時(shí)它是mutable的椒振,因此也直接作為最終轉(zhuǎn)換后的字符串昭伸。
/// range是要轉(zhuǎn)換的范圍,同時(shí)輸出轉(zhuǎn)換后改變的范圍澎迎,如果為NULL庐杨,視為全部轉(zhuǎn)換。
/// transform可以指定要進(jìn)行什么樣的轉(zhuǎn)換夹供,這里可以指定多種語言的拼寫轉(zhuǎn)換灵份。
/// reverse指定該轉(zhuǎn)換是否必須是可逆向轉(zhuǎn)換的。
/// 如果轉(zhuǎn)換成功就返回true哮洽,否則返回false
Boolean CFStringTransform(CFMutableStringRef string, CFRange *range, CFStringRef transform, Boolean reverse);
CFMutableStringRef string = CFStringCreateMutableCopy(NULL, 0, CFSTR("羋月"));
CFStringTransform(string, NULL, kCFStringTransformMandarinLatin, NO);
NSLog(@"%@",string);
/// 打印結(jié)果:mǐ yuè
/// 由于??正確的輸出了拼音填渠,而且還帶上了音標(biāo)。有時(shí)候我們不需要音標(biāo)怎么辦鸟辅?還好CFStringTransform同時(shí)提供了將音標(biāo)字母轉(zhuǎn)換為普通字母的方法kCFStringTransformStripDiacritics氛什。我們?cè)谏厦娴拇a基礎(chǔ)上再加上這個(gè):
CFStringTransform(string, NULL, kCFStringTransformStripDiacritics, NO);
NSLog(@"%@",string);
/// 打印結(jié)果:mi yue
由于后期考慮到,搜索模塊需要增加本地搜索聯(lián)系人的需求匪凉,所以本項(xiàng)目這里采用了內(nèi)部已經(jīng)封裝好 PinYin4Objc的HighlightedSearch屉更,它支持搜索關(guān)鍵字,高亮顯示洒缀,支持漢字、全拼、簡(jiǎn)拼搜索树绩,支持多音字搜索萨脑。
漢子轉(zhuǎn)拼音API如下:
/// WPFPinYinTools.h
/** 獲取傳入字符串的第一個(gè)拼音字母 */
+ (NSString *)firstCharactor:(NSString *)aString withFormat:(HanyuPinyinOutputFormat *)pinyinFormat;
數(shù)據(jù)處理整體代碼如下:
/// 聯(lián)系人數(shù)據(jù)處理
- (void)_handleContacts:(NSArray *)contacts {
if (MHObjectIsNil(contacts) || contacts.count == 0) return;
// 計(jì)算總?cè)藬?shù)
self.total = [NSString stringWithFormat:@"%ld位聯(lián)系人",contacts.count];
// 這里需要處理數(shù)據(jù)
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init];
// 獲取首字母
for(MHUser *contact in contacts){
// 存到字典中去 <ps: 由于 contacts.json 的wechatId 都是拼音 so...>
[tempDict setObject:contact forKey:[[contact.wechatId substringToIndex:1] uppercaseString]];
}
//排序,排序的根據(jù)是字母
NSComparator comparator = ^(id obj1, id obj2) {
if ([obj1 characterAtIndex:0] > [obj2 characterAtIndex:0]) {
return (NSComparisonResult)NSOrderedDescending;
}
if ([obj1 characterAtIndex:0] < [obj2 characterAtIndex:0]) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
};
// 已經(jīng)排好序的數(shù)據(jù)
NSMutableArray *letters = [tempDict.allKeys sortedArrayUsingComparator: comparator].mutableCopy;
NSMutableArray *viewModels = [NSMutableArray array];
/// 遍歷數(shù)據(jù)
for (NSString *letter in letters) {
// 存儲(chǔ)相同首字母 對(duì)象
NSMutableArray *temps = [[NSMutableArray alloc] init];
// 存到數(shù)組中去
for (NSInteger i = 0; i<contacts.count; i++) {
MHUser *contact = contacts[i];
if ([letter isEqualToString:[[contact.wechatId substringToIndex:1] uppercaseString]]) {
MHContactsItemViewModel *viewModel = [[MHContactsItemViewModel alloc] initWithContact:contact];
[temps addObject:viewModel];
}
}
[viewModels addObject:temps];
}
/// 需要配置 新的朋友饺饭、群聊渤早、標(biāo)簽、公眾號(hào)瘫俊、
MHContactsItemViewModel *friends = [[MHContactsItemViewModel alloc] initWithIcon:@"plugins_FriendNotify_36x36" name:@"新的朋友"];
MHContactsItemViewModel *groups = [[MHContactsItemViewModel alloc] initWithIcon:@"add_friend_icon_addgroup_36x36" name:@"群聊"];
MHContactsItemViewModel *tags = [[MHContactsItemViewModel alloc] initWithIcon:@"Contact_icon_ContactTag_36x36" name:@"標(biāo)簽"];
MHContactsItemViewModel *officals = [[MHContactsItemViewModel alloc] initWithIcon:@"add_friend_icon_offical_36x36" name:@"公眾號(hào)"];
// 插入到第一個(gè)位置
[viewModels insertObject:@[friends,groups,tags,officals] atIndex:0];
// 插入一個(gè)
[letters insertObject:UITableViewIndexSearch atIndex:0];
self.dataSource = viewModels.copy;
self.letters = letters.copy;
}
頁面展示
當(dāng)數(shù)據(jù)處理完鹊杖,構(gòu)建好cell
,刷新tableView
扛芽,理論上頁面展示和微信頁面應(yīng)該一模一樣??骂蓖。當(dāng)然我們滾動(dòng)到頁面的最底部,繼續(xù)上拉川尖,會(huì)露出tableView
的淺灰色(#ededed)
的背景色登下,但是看看微信的上拉,露出的卻是白色
的背景色叮喳,所以必須把這個(gè)細(xì)節(jié)加上去被芳。
實(shí)現(xiàn)邏輯非常簡(jiǎn)單,只需要設(shè)置tableViiew
的背景色為透明色
馍悟,然后添加一個(gè)白色
背景的UIView
在tableView
的下面即可畔濒,默認(rèn)隱藏,等有數(shù)據(jù)時(shí)才去顯示锣咒。實(shí)現(xiàn)代碼如下:
/// 添加一個(gè)tempView 放在最底下 用于上拉顯示白底
UIView *tempView = [[UIView alloc] init];
self.tempView = tempView;
// 默認(rèn)隱藏
tempView.hidden = YES;
tempView.backgroundColor = [UIColor whiteColor];
[self.view insertSubview:tempView belowSubview:self.tableView];
[self.tempView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).with.offset(0);
make.right.equalTo(self.view).with.offset(0);
make.bottom.equalTo(self.view).with.offset(0);
make.height.mas_equalTo(MH_SCREEN_HEIGHT * 0.5);
}];
Cell側(cè)滑備注
功能實(shí)現(xiàn)侵状,筆者這里采用iOS 11.0
提供的左滑刪除功能,只需實(shí)現(xiàn)UITableViewDelegate
即可宠哄。
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
return NO;
}
return YES;
}
- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0)){
UIContextualAction *remarkAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:@"備注" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
completionHandler(YES);
}];
UISwipeActionsConfiguration *config = [UISwipeActionsConfiguration configurationWithActions:@[remarkAction]];
config.performsFirstActionWithFullSwipe = NO;
return config;
}
由于最新微信側(cè)滑備注
是淺黑色(#4c4c4c)
壹将,而系統(tǒng)默認(rèn)的則是淺灰色
的,所以我們需要修改系統(tǒng)的樣式毛嫉,由于每次側(cè)滑诽俯,都有調(diào)用- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
這個(gè)API,而且我們利用Debug view Hierarchy
工具查看層級(jí)承粤,發(fā)現(xiàn)側(cè)滑是加在tableView
上暴区,而不是cell
上,所以解決思路如下:一旦調(diào)用此API辛臊,立即遍歷tableView
的subView
,然后找到對(duì)應(yīng)的UISwipeActionPullView
仙粱,修改其內(nèi)部的UISwipeActionStandardButton
背景色。
但是這里需要指出的是彻舰,由于存在兩種層級(jí)關(guān)系如下:
-
iOS 13.0+:
UITableView --> _UITableViewCellSwipeContainerView --> UISwipeActionPullView --> UISwipeActionStandardButton
-
iOS 13.0-:
UITableView --> UISwipeActionPullView --> UISwipeActionStandardButton
所以最終處理如下:
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath {
/// 注意低版本的Xcode中 不一定是 `_UITableViewCellSwipeContainerView+UISwipeActionPullView+UISwipeActionStandardButton` 而是 `UISwipeActionPullView+UISwipeActionStandardButton`
for (UIView *subView in tableView.subviews) {
if ([subView isKindOfClass:NSClassFromString(@"UISwipeActionPullView")]) {
subView.backgroundColor = MHColorFromHexString(@"#4c4c4c");
for (UIButton *button in subView.subviews) {
if ([button isKindOfClass:NSClassFromString(@"UISwipeActionStandardButton")]) {
// 修改背景色
button.backgroundColor = MHColorFromHexString(@"#4c4c4c");
}
}
} else if ([subView isKindOfClass:NSClassFromString(@"_UITableViewCellSwipeContainerView")]) {
for (UIView *childView in subView.subviews) {
if ([childView isKindOfClass:NSClassFromString(@"UISwipeActionPullView")]) {
childView.backgroundColor = MHColorFromHexString(@"#4c4c4c");
for (UIButton *button in childView.subviews) {
if ([button isKindOfClass:NSClassFromString(@"UISwipeActionStandardButton")]) {
// 修改背景色
button.backgroundColor = MHColorFromHexString(@"#4c4c4c");
}
}
}
}
}
}
}
當(dāng)然點(diǎn)擊備注
時(shí)伐割,也得修改其背景色候味,否則又會(huì)被重置為淺灰色
,代碼如下:
- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0)){
UIContextualAction *remarkAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:@"備注" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
sourceView.backgroundColor = MHColorFromHexString(@"#4c4c4c");
sourceView.superview.backgroundColor = MHColorFromHexString(@"#4c4c4c");
// Fixed Bug: 延遲一丟丟去設(shè)置 不然無效 點(diǎn)擊需要設(shè)置顏色 不然會(huì)被重置
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.001 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
sourceView.backgroundColor = MHColorFromHexString(@"#4c4c4c");
sourceView.superview.backgroundColor = MHColorFromHexString(@"#4c4c4c");
});
completionHandler(YES);
}];
UISwipeActionsConfiguration *config = [UISwipeActionsConfiguration configurationWithActions:@[remarkAction]];
config.performsFirstActionWithFullSwipe = NO;
return config;
}
當(dāng)然有興趣的同學(xué)也可以借助: MGSwipeTableCell 來實(shí)現(xiàn)側(cè)滑備注隔心。
關(guān)于白群,索引條A-Z
的實(shí)現(xiàn),筆者這里借助的是:SCIndexView 來實(shí)現(xiàn)的硬霍,關(guān)于其具體的實(shí)現(xiàn)帜慢,筆者這里就不再一一贅述了,有興趣的同學(xué)可以自行學(xué)習(xí)唯卖。
項(xiàng)目中的配置代碼如下粱玲,輕松實(shí)現(xiàn)微信索引Bar:
/// 監(jiān)聽數(shù)據(jù)
@weakify(self);
[[RACObserve(self.viewModel, letters) distinctUntilChanged] subscribeNext:^(NSArray * letters) {
@strongify(self);
if (letters.count > 1) {
self.tempView.hidden = NO;
}
self.tableView.sc_indexViewDataSource = letters;
self.tableView.sc_startSection = 1;
}];
#pragma mark - 初始化
- (void)_setup{
self.tableView.rowHeight = 56.0f;
self.tableView.backgroundColor = [UIColor clearColor];
// 配置索引模塊
SCIndexViewConfiguration *configuration = [SCIndexViewConfiguration configuration];
// 設(shè)置item 距離 右側(cè)屏幕的間距
configuration.indexItemRightMargin = 8.0;
// 設(shè)置item 文字顏色
configuration.indexItemTextColor = MHColorFromHexString(@"#555555");
// 設(shè)置item 選中時(shí)的背景色
configuration.indexItemSelectedBackgroundColor = MHColorFromHexString(@"#57be6a");
/// 設(shè)置索引之間的間距
configuration.indexItemsSpace = 4.0;
self.tableView.sc_indexViewConfiguration = configuration;
self.tableView.sc_translucentForTableViewInNavigationBar = true;
}
當(dāng)然通訊錄模塊中,還有個(gè)細(xì)節(jié)處理拜轨,那就是滾動(dòng)過程中抽减,懸浮HeaderView漸變
,主要涉及到背景色的漸變和文字顏色的漸變撩轰。當(dāng)然實(shí)現(xiàn)還是比較簡(jiǎn)單的胯甩,就是實(shí)現(xiàn)- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
方法,計(jì)算headerView.mh_y
的臨界點(diǎn)堪嫂。實(shí)現(xiàn)如下:
// UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
/// 刷新headerColor
[self _reloadHeaderViewColor];
}
/// 刷新header color
- (void)_reloadHeaderViewColor {
NSArray<NSIndexPath *> *indexPaths = self.tableView.indexPathsForVisibleRows;
for (NSIndexPath *indexPath in indexPaths) {
// 過濾
if (indexPath.section == 0) {
continue;
}
MHContactsHeaderView *headerView = (MHContactsHeaderView *)[self.tableView headerViewForSection:indexPath.section];
[self configColorWithHeaderView:headerView section:indexPath.section];
}
}
/// 配置 header color
- (void)configColorWithHeaderView:(MHContactsHeaderView *)headerView section:(NSInteger)section{
if (!headerView) {
return;
}
CGFloat insertTop = UIApplication.sharedApplication.statusBarFrame.size.height + 44;
CGFloat diff = fabs(headerView.frame.origin.y - self.tableView.contentOffset.y - insertTop);
CGFloat headerHeight = 33.0f;
double progress;
if (diff >= headerHeight) {
progress = 1;
}else {
progress = diff / headerHeight;
}
[headerView configColorWithProgress:progress];
}
/// MHContactsHeaderView.m
- (void)configColorWithProgress:(double)progress {
static NSMutableArray<NSNumber *> *textColorDiffArray;
static NSMutableArray<NSNumber *> *bgColorDiffArray;
static NSArray<NSNumber *> *selectTextColorArray;
static NSArray<NSNumber *> *selectBgColorArray;
if (textColorDiffArray.count == 0) {
UIColor *selectTextColor = MHColorAlpha(87, 190, 106, 1);
UIColor *textColor = MHColorAlpha(59, 60, 60, 1);
// 懸浮背景色
UIColor *selectBgColor = [UIColor whiteColor];
// 默認(rèn)背景色
UIColor *bgColor = MHColorAlpha(237, 237, 237, 1);
selectTextColorArray = [self getRGBArrayByColor:selectTextColor];
NSArray<NSNumber *> *textColorArray = [self getRGBArrayByColor:textColor];
selectBgColorArray = [self getRGBArrayByColor:selectBgColor];
NSArray<NSNumber *> *bgColorArray = [self getRGBArrayByColor:bgColor];
textColorDiffArray = @[].mutableCopy;
bgColorDiffArray = @[].mutableCopy;
for (int i = 0; i < 3; i++) {
double textDiff = selectTextColorArray[i].doubleValue - textColorArray[i].doubleValue;
[textColorDiffArray addObject:@(textDiff)];
double bgDiff = selectBgColorArray[i].doubleValue - bgColorArray[i].doubleValue;
[bgColorDiffArray addObject:@(bgDiff)];
}
}
NSMutableArray<NSNumber *> *textColorNowArray = @[].mutableCopy;
NSMutableArray<NSNumber *> *bgColorNowArray = @[].mutableCopy;
for (int i = 0; i < 3; i++) {
double textNow = selectTextColorArray[i].doubleValue - progress * textColorDiffArray[i].doubleValue;
[textColorNowArray addObject:@(textNow)];
double bgNow = selectBgColorArray[i].doubleValue - progress * bgColorDiffArray[i].doubleValue;
[bgColorNowArray addObject:@(bgNow)];
}
UIColor *textColor = [self getColorWithRGBArray:textColorNowArray];
self.letterLabel.textColor = textColor;
UIColor *bgColor = [self getColorWithRGBArray:bgColorNowArray];
self.contentView.backgroundColor = bgColor;
}
- (NSArray<NSNumber *> *)getRGBArrayByColor:(UIColor *)color
{
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char resultingPixel[4];
CGContextRef context = CGBitmapContextCreate(&resultingPixel, 1, 1, 8, 4, rgbColorSpace, (CGBitmapInfo)kCGImageAlphaNoneSkipLast);
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
CGContextRelease(context);
CGColorSpaceRelease(rgbColorSpace);
double components[3];
for (int component = 0; component < 3; component++) {
components[component] = resultingPixel[component] / 255.0f;
}
double r = components[0];
double g = components[1];
double b = components[2];
return @[@(r),@(g),@(b)];
}
- (UIColor *)getColorWithRGBArray:(NSArray<NSNumber *> *)array {
return [UIColor colorWithRed:array[0].doubleValue green:array[1].doubleValue blue:array[2].doubleValue alpha:1];
}
細(xì)節(jié)處理:由于要后期需要彈出 搜索模塊和收回搜索模塊偎箫,所以要保證滾動(dòng)到最頂部時(shí),要確保搜索框完全顯示或者完全隱藏皆串,否則就會(huì)導(dǎo)致在彈出搜索模塊淹办,然后收回搜索模塊,會(huì)導(dǎo)致動(dòng)畫不流暢恶复,影響用戶體驗(yàn)怜森,微信想必也是考慮到如此場(chǎng)景,可以說是谤牡,細(xì)節(jié)滿滿副硅。
解決方案也比較簡(jiǎn)單:判斷列表停止?jié)L動(dòng)后scrollView.contentOffset.y
是否在(-scrollView.contentInset.top, -scrollView.contentInset.top + searchBarH)
范圍內(nèi),判斷當(dāng)前是上拉還是下拉翅萤,上拉隱藏恐疲,下拉顯示。 代碼如下:
/// 細(xì)節(jié)處理:
/// 由于要彈出 搜索模塊套么,所以要保證滾動(dòng)到最頂部時(shí)培己,要確保搜索框完全顯示或者完全隱藏,
/// 不然會(huì)導(dǎo)致彈出搜索模塊,然后收回搜索模塊胚泌,會(huì)導(dǎo)致動(dòng)畫不流暢省咨,影響體驗(yàn),微信做法也是如此
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
/// 注意:這個(gè)方法不一定調(diào)用 當(dāng)你緩慢拖動(dòng)的時(shí)候是不會(huì)調(diào)用的
[self _handleSearchBarOffset:scrollView];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// 記錄剛開始拖拽的值
self.startDragOffsetY = scrollView.contentOffset.y;
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
// 記錄剛開始拖拽的值
self.endDragOffsetY = scrollView.contentOffset.y;
// decelerate: YES 說明還有速度或者說慣性玷室,會(huì)繼續(xù)滾動(dòng) 停止時(shí)調(diào)用scrollViewDidEndDecelerating
// decelerate: NO 說明是很慢的拖拽零蓉,沒有慣性笤受,不會(huì)調(diào)用 scrollViewDidEndDecelerating
if (!decelerate) {
[self _handleSearchBarOffset:scrollView];
}
}
/// 處理搜索框顯示偏移
- (void)_handleSearchBarOffset:(UIScrollView *)scrollView {
// 獲取當(dāng)前偏移量
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat searchBarH = 56.0f;
/// 在這個(gè)范圍內(nèi)
if (offsetY > -scrollView.contentInset.top && offsetY < (-scrollView.contentInset.top + searchBarH)) {
// 判斷上下拉
if (self.endDragOffsetY > self.startDragOffsetY) {
// 上拉 隱藏
CGPoint offset = CGPointMake(0, -scrollView.contentInset.top + searchBarH);
[self.tableView setContentOffset:offset animated:YES];
} else {
// 下拉 顯示
CGPoint offset = CGPointMake(0, -scrollView.contentInset.top);
[self.tableView setContentOffset:offset animated:YES];
}
}
}
以上就是微信通訊錄模塊所涉及到的全部知識(shí)點(diǎn),且難度一般壁公。當(dāng)然感论,通訊錄模塊
還有個(gè)重要功能--搜索
。??盡管筆者已經(jīng)在 WeChat 項(xiàng)目中實(shí)現(xiàn)了紊册,且效果跟微信如出一撤??。 但是考慮到其邏輯的復(fù)雜性快耿,以及UI的搭建等問題囊陡,后期筆者會(huì)單獨(dú)寫一篇文章,來詳細(xì)描述搜索模塊
的技術(shù)實(shí)現(xiàn)和細(xì)節(jié)處理掀亥。敬請(qǐng)期待...
期待
- 文章若對(duì)您有些許幫助撞反,請(qǐng)給個(gè)喜歡??,畢竟碼字不易搪花;若對(duì)您沒啥幫助遏片,請(qǐng)給點(diǎn)建議??,切記學(xué)無止境撮竿。
- 針對(duì)文章所述內(nèi)容吮便,閱讀期間任何疑問;請(qǐng)?jiān)谖恼碌撞吭u(píng)論指出幢踏,我會(huì)火速解決和修正問題髓需。
- GitHub地址:https://github.com/CoderMikeHe
- 源碼地址:WeChat
主頁
GitHub | 掘金 | CSDN | 知乎 |
---|---|---|---|
點(diǎn)擊進(jìn)入 | 點(diǎn)擊進(jìn)入 | 點(diǎn)擊進(jìn)入 | 點(diǎn)擊進(jìn)入 |