iOS纯Autolayout实现微信朋友圈和通讯录另附App启动页短视频效果

原创 2016年11月30日 22:42:01

       

2017/8/24更新 由于10.0以上的版本对于autolayout的布局有点变化,因此这里做一下修复 如果按照之前的操作,约束就会严重冲突,虽然不会影响对应的效果,但是看起来很不爽



1.首先更新下Using UITableView+FDTemplateLayoutCell (1.6) 去年用的是1.4,作者更新到了1.6,就是修复了这个bug

   2.对于约束的理解,报错无非就是约束的冲突,一个高度的改变,导致无法撑开整个cell的布局,因此这里修改下优先级,操作如下


    这里修改下对应1约束的优先级,默认和2一样是1000,把它改成999,也就是优先撑开内部空间的约束,再满足底部支撑,这样就没有报错了

根据个人的习惯而定,本博客主要以Autolayout为主,早之前没接触的时候,已经看习惯了代码布局UI,又长又

臭,而且主要是写出来不一定正确,跑起来的时候只有出一点错误,UI就飞了,一点都不直观,没错,这也是对立的

两派,由于习惯问题,很多人不愿意去接触Autolayout,但是它的存在真的很强大,首先考虑下微信微博发动态这类

布局UI,少说要上千行代码吧,但是Autolayout就可以为你省去那么多代码,你只要写逻辑就可以了,这样看上去非

常清晰,之前有写过一个Demo是简单介绍如何用Autolayout实现高度自适应的,比较简单,适合入门,需要的朋友可

以用力戳点击打开链接


     有个朋友需要让我写个朋友圈的Demo给他,正好晚上回去也没什么事,就写个微信Demo玩玩


启动页动画



朋友圈示例

             



图上的点赞之后数据源可能有点错乱,已经修改了

介绍下主要分析的知识点

1.微信朋友圈AutoLayout实现高度自适应

2.微信通讯路根据名字首字母分类排序

3.首次启动用短视频来做动画


首先给大家介绍个小小的框架,专门用来写设置页面这种老变来变去cell需求的框架

IASKAppSettingsViewController

最简单的做法就是跟着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];
}


其实主要还是涉及到不同子View之前的坐标转换到window上坐标的转换方式,理解不了的可以看点击打开链接


下面分析下通讯录里面实现搜索以及汉字转换成英文首字母排序

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


最后介绍下用短视频来做App首次启动的效果(类似keep uber 百灵鸟这些启动效果)
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];
}


这里我们在主控制的ViewwillAppear里面调用这些代码,通过NSUserdefault来进行判断是否第一次进来,最后我们把

装载有视频的控制器View增加到Window上面并且带到最前面展示,当用户点击屏幕或播放完进入App的时候移除

2016-11-30 21:16:58.250 MKJWechat[3372:55057] dealloc-->AnimationVideoViewController


看到有打印就已经释放了,搞定!!!




详细Demo:点击打开链接


有需要改进的地方大家尽情留言,小弟下班时间抽空写的,没那么完善,各位还是自行跑Demo试试吧,我会不断完

善的,再多点时间就把即时通信也加一下进去,自己正好也学习下 

蹦了一定要给我留言哦,我好完善一下






版权声明:本文为博主原创文章,未经博主允许不得转载。转载请Email我.......

iOS 多图启动页 短视频启动功能实现

[转载] 多张启动页图片、启动添加短视频功能,需要导入助手类文件 下载地址:https://github.com/dangxiaoyin/XZMCoreNewFeature ...
  • lichuanliangios
  • lichuanliangios
  • 2016年09月28日 14:31
  • 1256

iOS之iPhone手机通讯录和短信搜索界面的实现以及UISearchController和UISearchDisplayController的浅析

本来觉得这个模块也就是一个SearchBar就搞定了,但是现在的产品经理也是够了,一会儿一个想法,之前的搜索 都已经写完了,类似主流的电商,好像也没那么麻烦,但是改版了总得弄点什么吧。嘿,哥们,我现在...
  • Deft_MKJing
  • Deft_MKJing
  • 2016年07月17日 12:23
  • 4425

iOS中Xcode使用UIScrollView+AutoLayout轻松实现滚动布局

对于一些屏幕尺寸比较小的手机,或者内容很长,一屏幕显示不了的情况,我们通常可以用手指往上滑的方法浏览底部内容,如果不是用ListView或者UITableView去实现的话,我们就需要自己实现滚动布局...
  • hwe_xc
  • hwe_xc
  • 2016年04月25日 11:16
  • 3980

带你实现开发者头条(一) 启动页实现

对于很多初学者或者刚工作的Android新手来说,我们的项目经验还停留在做demo的阶段,有没有一种很low的感觉,并且当你真正上手做大项目的时候又不知道把自己学到的东西用上去。。有一种自己家里有座金...
  • lowprofile_coding
  • lowprofile_coding
  • 2016年04月16日 21:21
  • 4851

IOS AutoLayout详解(三)用代码实现(附Demo下载)

原创Blog,转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的IOS SDK专栏,这个专栏我会持续进行更新。 IOS SDK详解前言: 在开发的过程中,有时候创建...
  • Hello_Hwc
  • Hello_Hwc
  • 2015年03月02日 09:54
  • 3825

作为新手,关于APP启动时出现空白页才跳到启动页,解决办法如下

一、找到启动的第一个页面,可以在AndroidManifest.xml配置,也可以在代码里面配置他的主题即可。      activity android:name=".guide" andro...
  • twopai
  • twopai
  • 2017年03月20日 18:07
  • 525

iOS 程序图标AppIcon和启动页面LaunchImage设置 —— HERO博客

iOS 程序图标AppIcon和启动页面LaunchImage设置
  • hero_wqb
  • hero_wqb
  • 2015年11月12日 21:50
  • 4071

iOS APP 启动页面的使用

APP启动时候我们经常看到一个启动页面,我知道的有一下两种做法: LaunchScreen.storyboard 的运用 点击LaunchScreen.storyboard,用IB为Lau...
  • Ashimar_a
  • Ashimar_a
  • 2016年06月13日 11:26
  • 4502

iOS 启动页加入动态的广告实现

做产品的时候有的时候需要在程序启动的时候加入广告,不难。只要在appdelegate.m 的启动加载完成方法加入需要的广告视图就可以了,但是有的时候需要让启动图看起来就是一个广告,只有一个界面。而且还...
  • a158337
  • a158337
  • 2016年01月08日 08:59
  • 2223

iOS app启动播放视频实现

http://blog.csdn.net/piao_polar/article/details/8923828 背景     一般的app,启动的时候是显示一张底图的。但运营提出要播放...
  • jmulxg
  • jmulxg
  • 2015年05月26日 17:31
  • 2959
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:iOS纯Autolayout实现微信朋友圈和通讯录另附App启动页短视频效果
举报原因:
原因补充:

(最多只允许输入30个字)