根据个人的习惯而定,本博客主要以Autolayout为主,早之前没接触的时候,已经看习惯了代码布局UI,又长又
臭,而且主要是写出来不一定正确,跑起来的时候只有出一点错误,UI就飞了,一点都不直观,没错,这也是对立的
两派,由于习惯问题,很多人不愿意去接触Autolayout,但是它的存在真的很强大,首先考虑下微信微博发动态这类
布局UI,少说要上千行代码吧,但是Autolayout就可以为你省去那么多代码,你只要写逻辑就可以了,这样看上去非
常清晰,之前有写过一个Demo是简单介绍如何用Autolayout实现高度自适应的,比较简单,适合入门,需要的朋友可
以用力戳点击打开链接
有个朋友需要让我写个朋友圈的Demo给他,正好晚上回去也没什么事,就写个微信Demo玩玩
启动页动画
朋友圈示例
介绍下主要分析的知识点
1.微信朋友圈AutoLayout实现高度自适应
2.微信通讯路根据名字首字母分类排序
3.首次启动用短视频来做动画
首先给大家介绍个小小的框架,专门用来写设置页面这种老变来变去cell需求的框架
最简单的做法就是跟着demo来一遍,配置你需要的Plist文件,把每个cell需要展示的信息存起来
用的时候你只要在控制器根据之前plist里面配置好的key来读就可以了,非常容易修改配置,免得需求该来该去麻烦
- (void)settingsViewController:(IASKAppSettingsViewController*)sender tableView:(UITableView *)tableView didSelectCustomViewSpecifier:(IASKSpecifier*)specifier { NSIndexPath * indexPath = [sender.settingsReader indexPathForKey:specifier.key]; [tableView deselectRowAtIndexPath:indexPath animated:YES]; CASE_TYPE type = [self caseIndexForKey:specifier.key]; switch (type) { case FRIENTDCIRCLE: { DiscoveryViewController *discoverVC = [[DiscoveryViewController alloc] init]; [self.navigationController pushViewController:discoverVC animated:YES]; } break; case SHAKEANDSHAKE: break; case YAOYIYAO: break; case NEARBYPEOPLE: break; case SHOPPING: break; case GAME: break; default: break; } }
进入正题,用Autolayout来布局高度自适应的朋友圈,控制的还是tableview,最关键的还是那个cell的约束设置,直
接看约束图
一套完整的约束必须是上下左右各边都有约束到,上面复杂约束已经做到了上左下右都有约束,如果有一边漏了,虽
然约束不会报错不会警告,但是如果你不完整,你到时候根本无法计算,看下最简单的约束示例
那么问题来了,布局完了是非常的简单高效,这应该不难吧,觉得难的看我一开始给的链接,那个入门级别的
1.label如何实现自适应高度
2.collectionView如何根据不同图片个数增加高度
3.评论的tableview如何动态变化
注:这里tableview的cell里面嵌了tableview和collectionView,那么这两个的代理方法就不介绍了,主要看如何动态变化,需要看的同学到时候自己下载代码跑起来看看就好了
关键代码如下
- (void)configCell:(MKJFriendTableViewCell *)cell indexpath:(NSIndexPath *)indexpath { __weak typeof(cell)weakCell = cell; FriendIssueInfo *issueInfo = self.friendsDatas[indexpath.row]; // headerImage 头像 实现渐隐效果 [cell.headerImageView sd_setImageWithURL:[NSURL URLWithString:issueInfo.photo] placeholderImage:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { if (image && cacheType == SDImageCacheTypeNone) { weakCell.headerImageView.alpha = 0; [UIView animateWithDuration:0.8 animations:^{ weakCell.headerImageView.alpha = 1.0f; }]; } else { weakCell.headerImageView.alpha = 1.0f; } }]; // name 名字 cell.nameLabel.text = issueInfo.userName; // description 描述 根据配置在数据源的是否展开字段确定行数 cell.desLabel.text = issueInfo.message; cell.isExpand = issueInfo.isExpand; if (issueInfo.isExpand) { cell.desLabel.numberOfLines = 0; } else { cell.desLabel.numberOfLines = 3; } // 全文label 根据文字的高度是否展示全文lable 点击事件通过数据源来交互 CGSize rec = [issueInfo.message boundingRectWithSize:CGSizeMake(SCREEN_WIDTH - 90, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont boldSystemFontOfSize:14]} context:nil].size; if (rec.height > 55) { cell.showAllDetailsButton.hidden = NO; cell.showAllHeight.constant = 15; } else { cell.showAllHeight.constant = 0; cell.showAllDetailsButton.hidden = YES; } // img 九宫格图片,用collectionView做 cell.imageDatas = [[NSMutableArray alloc] initWithArray:issueInfo.messageSmallPics]; cell.imageDatasBig = [[NSMutableArray alloc] initWithArray:issueInfo.messageBigPics]; [cell.collectionView reloadData]; // 这里可以用lauout来获取其高度,但是由于嵌套的关系,可能算不准,我们还是手动算好了 // [cell.collectionView layoutIfNeeded]; // cell.colletionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height; CGFloat width = SCREEN_WIDTH - 90 - 20; // 没图片就高度为0 (约束是可以拖出来的哦哦) if ([NSArray isEmpty:issueInfo.messageSmallPics]) { cell.colletionViewHeight.constant = 0; } else { if (issueInfo.messageSmallPics.count == 1) { cell.colletionViewHeight.constant = width / 1.5; } else { cell.colletionViewHeight.constant = ((issueInfo.messageSmallPics.count - 1) / 3 + 1) * (width / 3) + (issueInfo.messageSmallPics.count - 1) / 3 * 15; } } // timeStamp 时间 cell.timeLabel.text = issueInfo.timeTag; // right action button 弹出黑色点赞或者评论的框 cell.isShowPopView = NO; cell.backPopViewWidth.constant = 0; // right action button inline like button state 按钮状态也是根据数据源配置 if (issueInfo.hadClickLike) { [cell.likeButton setTitle:@"取消" forState:UIControlStateNormal]; } else { [cell.likeButton setTitle:@"赞" forState:UIControlStateNormal]; } cell.hadLikeActivityMessage = issueInfo.hadClickLike; // 默认都是没有点赞 // commentTableView 评论的taibleView // 这里的思路也是可以根据contentSize获取,但是貌似又可能算不准,我还是手动计算,思路就是 // 最后一个cell的Y轴坐标加上其高度就是算出来的高度啦 cell.commentdatas = [[NSMutableArray alloc] initWithArray:issueInfo.commentMessages]; [cell.commentTableView reloadData]; CGRect recHeight = CGRectZero; if (![NSArray isEmpty:issueInfo.commentMessages]) { recHeight = [cell.commentTableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:issueInfo.commentMessages.count - 1 inSection:0]]; } cell.tableViewHeight.constant = recHeight.origin.y + recHeight.size.height; // NSLog(@"%@,heightTable%f",indexpath,cell.tableViewHeight.constant); }
注:可能要备注下,这里的collectionView和tableview的约束高度是可以拖出来重新赋值的,同学一定要记得啊,毕
竟动态高度,需要根据数据源来配置,而且这里点赞,收起什么的都用数据源来配置了
OK,根据上面的完美约束以及代码动态算了高度(这里内嵌的两个tableview和collection都可以用contentsize来获
取高度哦,也可以像我这样自己算),直接调用下面一句代码,高度自适应完美搞定!!!
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return [tableView fd_heightForCellWithIdentifier:identify cacheByIndexPath:indexPath configuration:^(MKJFriendTableViewCell *cell) { [self configCell:cell indexpath:indexPath]; }];
功能点还是蛮多的
1.点赞
2.评论
3.文字展开收缩
4.图片展示
5.楼中楼评论
6.这么多都会涉及到复用,需要注意呀同学们,而且这是Demo,还有很多地方需要优化啊
简单介绍一种评论的键盘问题,需要更多的自行拉倒最下面去下载就好了
计算弹起高度看图就可以明白了,再配合下代码即可
#pragma mark - 键盘的代理 show or hidden - (void)keyboardWillShow:(NSNotification *)noti { self.isKeyboardShowing = YES; NSDictionary *userInfo = noti.userInfo; CGFloat keyboardHeight = [[userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; CGFloat delta = 0; CGFloat topOffsetKeyboard = 0; if (self.isResponseByCell) { // 是通过点击cell触发键盘 topOffsetKeyboard = [UIScreen mainScreen].bounds.size.height - keyboardHeight - kChatToolBarHeight - self.selectedCellHeight - 20; } else // 点击comment触发 { topOffsetKeyboard = [UIScreen mainScreen].bounds.size.height - keyboardHeight - kChatToolBarHeight - 30; } delta = self.touch_offset_y - topOffsetKeyboard; CGFloat contentOffset = self.tableView.contentOffset.y; // 这个指的是顶部tableView滚动的距离 contentOffset += delta; // delta + 下拉 -上拉 if (contentOffset <= -64) { contentOffset = -64; } [self.tableView setContentOffset:CGPointMake(self.tableView.contentOffset.x, contentOffset) animated:YES]; }
下面分析下通讯录里面实现搜索以及汉字转换成英文首字母排序
1.搜索控制器UISearchController点击打开链接
self.searchResult = [[SearchResultController alloc] init]; self.searchResult.block = ^{ [weakSelf.searchController.searchBar resignFirstResponder]; }; self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.searchResult]; self.searchController.searchResultsUpdater = self; self.searchController.searchBar.placeholder = @"查找联系人"; self.searchController.searchBar.delegate = self; self.searchController.searchBar.showsCancelButton = NO; self.searchController.searchBar.returnKeyType = UIReturnKeySearch; self.searchController.searchBar.backgroundColor = RGBA(153, 153, 153, 1); self.searchController.searchBar.backgroundImage = [UIImage new]; UITextField *searchBarTextField = [self.searchController.searchBar valueForKey:@"_searchField"]; if (searchBarTextField) { [searchBarTextField setBackgroundColor:[UIColor whiteColor]]; [searchBarTextField setBorderStyle:UITextBorderStyleRoundedRect]; searchBarTextField.layer.cornerRadius = 5.0f; searchBarTextField.layer.borderColor = RGBA(204, 204, 204, 1).CGColor; searchBarTextField.layer.borderWidth = 0.5f; } self.tableView.tableHeaderView = self.searchController.searchBar;
2.代理实现搜索结果显示
// 搜索的代理 - (void)updateSearchResultsForSearchController:(UISearchController *)searchController { NSLog(@"%@",searchController.searchBar.text); NSString *searchString = [self.searchController.searchBar text]; if (self.filterFirends!= nil) { [self.filterFirends removeAllObjects]; } if ([PinyinHelper isIncludeChineseInString:searchString]) { for (FriendInfo *friend in self.allFriends) { if ([friend.userName containsString:searchString]) { [self.filterFirends addObject:friend]; } } } else { for (FriendInfo *friend in self.allFriends) { HanyuPinyinOutputFormat *outputFormat=[[HanyuPinyinOutputFormat alloc] init]; outputFormat.vCharType = VCharTypeWithV; outputFormat.caseType = CaseTypeLowercase; outputFormat.toneType = ToneTypeWithoutTone; NSString *outputPinyin=[PinyinHelper toHanyuPinyinStringWithNSString:friend.userName withHanyuPinyinOutputFormat:outputFormat withNSString:@""]; if ([[outputPinyin uppercaseString] containsString:[searchString uppercaseString]]) { [self.filterFirends addObject:friend]; } } } //过滤数据 self.searchResult.filterData = self.filterFirends; //刷新表格 [self.searchResult refreshData]; }
3.这里加载出来的数据源是要根据字母筛选的 A--数组 B--数组 ......Z--数组,这就是基本结构
// 处理英文首字母 - (void)handleFirstLetterArray { // 拿到所有的key 字母 NSMutableDictionary *letterDict = [[NSMutableDictionary alloc] init]; for (FriendInfo *friend in self.allFriends) { HanyuPinyinOutputFormat *outputFormat=[[HanyuPinyinOutputFormat alloc] init]; outputFormat.vCharType = VCharTypeWithV; outputFormat.caseType = CaseTypeLowercase; outputFormat.toneType = ToneTypeWithoutTone; NSString *outputPinyin=[PinyinHelper toHanyuPinyinStringWithNSString:friend.userName withHanyuPinyinOutputFormat:outputFormat withNSString:@""]; NSLog(@"%@",outputPinyin); [letterDict setObject:friend forKey:[[outputPinyin substringToIndex:1] uppercaseString]]; } // 字母数组 self.letterArr = letterDict.allKeys; // 让key进行排序 A - Z self.letterArr = [[NSMutableArray alloc] initWithArray:[self.letterArr sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { // 大于上升,小于下降 return [obj1 characterAtIndex:0] > [obj2 characterAtIndex:0]; }]]; // 遍历所有的排序后的key 每个Key拿到对应的数组进行组装 for (NSString *letter in self.letterArr) { NSMutableArray *nameArr = [[NSMutableArray alloc] init]; for (FriendInfo *friend in self.allFriends) { HanyuPinyinOutputFormat *outputFormat=[[HanyuPinyinOutputFormat alloc] init]; outputFormat.vCharType = VCharTypeWithV; outputFormat.caseType = CaseTypeUppercase; outputFormat.toneType = ToneTypeWithoutTone; NSString *outputPinyin=[PinyinHelper toHanyuPinyinStringWithNSString:friend.userName withHanyuPinyinOutputFormat:outputFormat withNSString:@""]; if ([letter isEqualToString:[[outputPinyin substringToIndex:1] uppercaseString]]) { [nameArr addObject:friend]; } } // 根据key装大字典 // A -- 一批通讯人 // B -- 一批人 // ... [self.nameDict setObject:nameArr forKey:letter]; } }
1.首先弄个MP4格式的本地小视频,用一个viewcontroller来做视频容器,用MPMoviePlayer来做视频
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:self.videoURL]; self.moviePlayer.view.frame = CGRectMake(0, 点击打开链接0, SCREEN_WIDTH, SCREEN_HEIGHT); self.moviePlayer.shouldAutoplay = YES; self.moviePlayer.controlStyle = MPMovieControlStyleNone; self.moviePlayer.repeatMode = MPMovieRepeatModeNone; self.moviePlayer.movieSourceType = MPMovieSourceTypeFile; [self.view addSubview:self.moviePlayer.view]; [self configShimmerLabel]; //监听播放完成 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(playFinsihed) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
2.这里视频底部有一串闪烁的文字,这里用的是facebook的一个动画效果 点击打开链
就不展开介绍了,非常简单,需要看的朋友可以点链接
3.调用时机以及如何展示层级
- (void)jungleIfFirstLoading { __weak typeof(self)weakSelf = self; NSInteger firstIN = [[[NSUserDefaults standardUserDefaults] valueForKey:@"FIRST_ENTER_IN"] integerValue]; if (firstIN != 0) { return; } self.animationVC = [[AnimationVideoViewController alloc] init]; self.animationVC.videoURL = [NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:@"intro"ofType:@"mp4"]]; self.animationVC.view.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); self.animationVC.finishBlock = ^{ [UIView animateWithDuration:1.0 animations:^{ weakSelf.animationVC.view.alpha = 0; } completion:^(BOOL finished) { [weakSelf.animationVC.view removeFromSuperview]; weakSelf.animationVC = nil; }]; }; [[[UIApplication sharedApplication] keyWindow] addSubview:self.animationVC.view]; [[[UIApplication sharedApplication] keyWindow] bringSubviewToFront:self.animationVC.view]; [[NSUserDefaults standardUserDefaults] setValue:@(1) forKey:@"FIRST_ENTER_IN"]; [[NSUserDefaults standardUserDefaults] synchronize]; }
装载有视频的控制器View增加到Window上面并且带到最前面展示,当用户点击屏幕或播放完进入App的时候移除
2016-11-30 21:16:58.250 MKJWechat[3372:55057] dealloc-->AnimationVideoViewController
详细Demo:点击打开链接
有需要改进的地方大家尽情留言,小弟下班时间抽空写的,没那么完善,各位还是自行跑Demo试试吧,我会不断完
善的,再多点时间就把即时通信也加一下进去,自己正好也学习下