Messages是iOS系统中原生的信息应用,其既能够经过运营商网络发送短信息,也能够经过互联网进行相似微信类社交软件的即时聊天。可是因为其封闭性与功能的单一,使用其进行即时聊天的用户并很少。随着iOS10系统的推出,或许能够改变这一现状。在iOS10中,Messages的功能被扩展的十分强大,经过Messages,用户能够分享图片,音乐,视频,能够随手涂鸦,使用自定义的表情包,能够进行Apple Pay支付,购物,甚至能够在Messages中玩游戏。而且,上面所提到的这些功能都全面开发出了接口供开发者进行开发与扩展。微信
在iOS10中,开发者能够进行与Messages相关的开发有两类:独立的Messages应用与Messages应用扩展。其中,Messages应用扩展须要依附一个宿主App而存在。不管哪一种类型的Messages应用,其都又分为两类,StickerPicks(表情包)与iMessage Apps(Messages应用)。网络
Sticker Picks可谓是iOS10中一个十分强大的新功能。在iOS10系统的iPhone上,Messages应用中会内嵌一个Message App Store,用户能够直接从里面下载针对于Messages的独立表情包和独立第三方应用。开发者也能够独立开发表情包发布到这个Message App Store中。session
开发Sticker Picks表情包十分简单,开发者能够不用写一句代码,将整理好的表情进行打包提交便可完成。使用Xcode8建立一个新的工程,选择Sticker Pack Application模板,以下图所示:app
建立出工程后,能够发现模板中没有任何代码文件,只有一个Stickers.xcstickers包。将准备好的表情包图片导入这个Stickers中,其中支持静态图片,也支持动态表情gif图片。关于导入的图片,有以下几条规则:框架
1.图片文件的格式必须是PNG、APNG、GIF或者JPEG。工具
2.单个文件的大小不能超过500KB。布局
3.最优的效果是当图片尺寸在100*100到206*206之间。动画
注意:在提供图片的时候,开发者只须要提供@3倍图便可,即最优尺寸在300*300到618*618之间的图片。系统会自动生成@2与@1倍图。this
开发的表情包会显示在Messages应用的工具中,须要注意,在表情列表的排版中,每一个表情缩略图只支持3种尺寸的排版,对应的尺寸分别以下:atom
Small类型:100*100
Medium类型:136*136
Large类型:206*206
在Xcode中,能够对要使用的模板进行选择,以下图:
在模拟器中运行工程,Messages中效果以下图:
和普通iOS应用程序同样,将设备选择为Generic iOS Device后直接Archives便可将表情包提交到AppStore,审核经过后,便可在Message App Store中进行下载。
小提示:其实StickerPicks翻译成表情包并不合适,其更有一层贴纸的概念。实际上其也确实有贴纸的功能,在Messages应用中,用户能够经过长按移动手势,来将某个Sticker添加在另外一个Sticker上面。以下图:
扩展表情包与独立表情包最大的不一样在于扩展须要寄宿于某个宿主App中,建立扩展target,选择Sticker Pick Extension,以下图,以后和独立表情包开发过程一致。
StickerPicks的图标和宿主App并不共用,其须要一套独特尺寸的icon,尺寸以下:
效果以下图所示:
和StickerPicks表情包同样,Messages App也分为独立应用与扩展两种。其实它们的开发思路和方法彻底一致,只是有无宿主App的区别。
开发Messages App须要使用到iOS中引入的一个新的开发框架Messages。Messages比较简单,其中涉及到的类并不十分多,下图中概述了其中重要的类和之间的关系:
MSMessageAppViewController:这个类Messages App的基础视图控制器类,其继承自UIViewController,但其中添加了许多Messages App相关的声明周期方法。
MSConversation:描述一个会话实例。
MSSticker:表情贴图实例。
MSMessage:在Messages App之间进行传递的消息实体。
MSMessageLayout:抽象类,其并无实现任何方法,有子类实现。
MSMessageTemplateLayout:用于对消息实体MSMessage进行布局排版。
MSStickerBorwserViewController:用于建立表情包视图控制器。
MSStickerBorwserView:表情包视图容器,相似CollectionView。
MSStickerView:表情承载视图。
使用Xcode新建一个Messages App工程以下:
其会自动生成一个MessagesViewController类,这个类就是此Messages App的主界面视图控制器。须要注意,Messages App的视图控制器都分为两种状态,分别为Compact(紧凑的)和Expanded(扩宽的)。而且在这两种状态进行切换时,视图的底部的工具栏和头部的导航栏也会交替出现,这致使了即便是使用自动布局,依然没法完美的解决Messages App布局的统一性,须要手动进行调整处理,后面会介绍到。
在MessagesViewController类中添加其余视图控件,大部分iOS App开发中可使用的UI控件这里均可以使用,可是有一点须要注意,对于能够弹出键盘的UI控件,例如UITextView与UITextField,当Messages App界面处理Compact模式时,键盘是不能弹出的,只有当界面处于Expanded模式时,键盘才被容许弹出。
为了使Messages App的界面在任何模式下都能保持统一,须要手动对其中视图约束进行修改,示例代码以下:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.dataArray = [NSMutableArray array]; [self.dataArray addObjectsFromArray:@[@"发送文本信息",@"插入表情",@"插入文件",@"插入消息实体",@"跳转第二个界面",@"贴图包"]]; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cellId"]; self.tableView.dataSource = self; self.tableView.delegate = self; self.session = [[MSSession alloc]init]; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.dataArray.count; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cellId"]; cell.textLabel.text = self.dataArray[indexPath.row]; return cell; } //这个方法在Messages App加载完成处于活跃状态时被调用 在其中根据模式设置布局参数 -(void)didBecomeActiveWithConversation:(MSConversation *)conversation { // Called when the extension is about to move from the inactive to active state. // This will happen when the extension is about to present UI. if (self.presentationStyle==MSMessagesAppPresentationStyleCompact) { _topMargan.constant = 0; _leftMargan.constant = -15; _rightMargan.constant = -15; _bottomMargan.constant=-44; }else{ _topMargan.constant = -85; _leftMargan.constant = -15; _rightMargan.constant = -15; _bottomMargan.constant=0; } [self.view layoutIfNeeded]; } //这个方法在视图控制器的模式发生了改变时调用 在其中根据模式修改布局参数 -(void)didTransitionToPresentationStyle:(MSMessagesAppPresentationStyle)presentationStyle { // Called after the extension transitions to a new presentation style. // Use this method to finalize any behaviors associated with the change in presentation style. if (presentationStyle==MSMessagesAppPresentationStyleCompact) { _topMargan.constant = 0; _leftMargan.constant = -15; _rightMargan.constant = -15; _bottomMargan.constant=-44; }else{ _topMargan.constant = -85; _leftMargan.constant = -15; _rightMargan.constant = -15; _bottomMargan.constant=0; } [self.view layoutIfNeeded]; }
因为MSMessagesAppViewController类是继承于UIViewController类的,所以UIViewController中的视图控制器切换方法这里均可以直接使用,MSMessagesAppViewController中供开发者进行调用的属性和方法以下:
//当前激活的会话实例 后面会介绍 @property (nonatomic, strong, readonly, nullable) MSConversation *activeConversation; //当前界面所在的状态 /* typedef NS_ENUM(NSUInteger, MSMessagesAppPresentationStyle) { //紧凑状态 MSMessagesAppPresentationStyleCompact, //扩宽状态 MSMessagesAppPresentationStyleExpanded } NS_ENUM_AVAILABLE_IOS(10_0); */ @property (nonatomic, assign, readonly) MSMessagesAppPresentationStyle presentationStyle; //切换界面状态 -(void)requestPresentationStyle:(MSMessagesAppPresentationStyle)presentationStyle; //调用此方法后,MessagesApp被收回 弹出键盘 -(void)dismiss;
MSMessagesAppViewController中新增长的声明周期方法以下:
//当Messages App将要激活时调用 -(void)willBecomeActiveWithConversation:(MSConversation *)conversation; //当Messages App已经被激活后调用 -(void)didBecomeActiveWithConversation:(MSConversation *)conversation; //当Messages App将要被注销时调用 -(void)willResignActiveWithConversation:(MSConversation *)conversation; //当MessageApp已经被注销时调用 -(void)didResignActiveWithConversation:(MSConversation *)conversation; //消息实体在会话中将要被选中时调用 -(void)willSelectMessage:(MSMessage *)message conversation:(MSConversation *)conversation; //消息实体在会话中已经被选中时调用 -(void)didSelectMessage:(MSMessage *)message conversation:(MSConversation *)conversation; //接收到同一Messages App发送的消息实体时调用 -(void)didReceiveMessage:(MSMessage *)message conversation:(MSConversation *)conversation; //开发发送消息时调用 -(void)didStartSendingMessage:(MSMessage *)message conversation:(MSConversation *)conversation; //取消发送时调用 -(void)didCancelSendingMessage:(MSMessage *)message conversation:(MSConversation *)conversation; //控制器界面模式将要改变时调用 -(void)willTransitionToPresentationStyle:(MSMessagesAppPresentationStyle)presentationStyle; //控制器界面已经改变时调用 -(void)didTransitionToPresentationStyle:(MSMessagesAppPresentationStyle)presentationStyle;
MSConversation类用来描述会话,MSMessagesAppViewController中内置MSConversation对象,开发者能够用它来进行消息传递,示例代码以下:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ switch (indexPath.row) { case 0: { [self.activeConversation insertText:@"新的信息" completionHandler:^(NSError * error) { }]; } break; case 1: { [self.activeConversation insertSticker:[[MSSticker alloc] initWithContentsOfFileURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:@"productImage" ofType:@"png"]] localizedDescription:@"image" error:nil] completionHandler:nil]; } break; case 2: { [self.activeConversation insertAttachment:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:@"file" ofType:nil]] withAlternateFilename:@"文件" completionHandler:nil]; } break; case 3: { MSMessage * message = [[MSMessage alloc]initWithSession:_session]; message.URL = [NSURL URLWithString:@"http://www.baidu.com"]; message.accessibilityLabel = @"message"; message.summaryText = @"message"; MSMessageTemplateLayout * layout = [[MSMessageTemplateLayout alloc]init]; layout.caption = @"caption"; layout.subcaption = @"subcaption"; layout.trailingCaption = @"trailing"; layout.trailingSubcaption =@"subtrailing"; layout.image = [UIImage imageNamed:@"productImage"]; layout.mediaFileURL =[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:@"productImage" ofType:@"png"]]; layout.imageTitle = @"杜康"; layout.imageSubtitle = @"酒水"; message.layout = layout; [self.activeConversation insertMessage:message completionHandler:nil]; } break; case 4: { [self presentViewController:[StackerViewController new] animated:YES completion:nil ]; } break; case 5: { [self presentViewController:[StackController new] animated:YES completion:nil ]; } break; default: break; } }
MSConversation支持发送的消息分为4中,分别为文本消息,表情贴图消息,文件消息和Message实体消息,上面代码都作了演示。MSConversation中重要属性和方法解析以下:
//本地的设备UUID @property (nonatomic, readonly) NSUUID *localParticipantIdentifier; //会话中远程设备的UUID 支持多人会话 @property (nonatomic, readonly) NSArray<NSUUID *> *remoteParticipantIdentifiers; //当前选中的消息实体 @property (nonatomic, readonly, nullable) MSMessage *selectedMessage; //插入文本消息 - (void)insertText:(NSString *)text completionHandler:(nullable void (^)(NSError * _Nullable))completionHandler; //插入表情贴图消息 - (void)insertSticker:(MSSticker *)sticker completionHandler:(nullable void (^)(NSError * _Nullable))completionHandler; //插入文件附件消息 - (void)insertAttachment:(NSURL *)URL withAlternateFilename:(nullable NSString *)filename completionHandler:(nullable void (^)(NSError * _Nullable))completionHandler; //插入Message实体消息 - (void)insertMessage:(MSMessage *)message completionHandler:(nullable void (^)(NSError * _Nullable))completionHandler;
效果图以下:
MSMessage是Messages App定义的一种消息实体,其能够用来在Messages App间传递信息,由于它的存在,经过Messages用用实现休闲对战游戏,棋牌游戏变得十分容易,开发者不须要在写即时通讯连接,只需设计游戏逻辑便可。MSMessage不可以彻底自定义UI,可是Messages框架中的MSMessageTemplateLayout类能够对其UI进行简单的配置。
MSMessage类中经常使用的属性和方法以下:
//初始化方法 能够绑定一个session,同一个session种的消息实体会被归为一类 -(instancetype)initWithSession:(MSSession *)session NS_DESIGNATED_INITIALIZER; //发送此消息的设备 @property (nonatomic, readonly) NSUUID *senderParticipantIdentifier; //消息的UI布局信息 @property (nonatomic, copy, nullable) MSMessageLayout* layout; //消息附带的URL 开发者能够经过这个URL来传值 @property (nonatomic, copy, nullable) NSURL *URL; //是否保留过时的消息 @property (nonatomic, assign) BOOL shouldExpire; //盲人模式中对应的文案 @property (nonatomic, copy, nullable) NSString *accessibilityLabel;
前面介绍,MSMessage类中并无定义UI,UI部分须要配合MSMessageLayout类来配置。须要注意,MSMessageLayout类是一个抽象类,apple设计的目的多是为了之后便于扩展多个消息布局模板。目前,开发者只须要使用MSMessageTemplateLayout类来对消息实体进行布局。
MSMessageTemplateLayout类中能够配置的属性以下:
//设置消息实体的标题 @property (nonatomic, copy, nullable) NSString *caption; //设置消息实体的子标题 @property (nonatomic, copy, nullable) NSString *subcaption; //设置消息实体的右侧标题 @property (nonatomic, copy, nullable) NSString *trailingCaption; //设置消息实体的右侧子标题 @property (nonatomic, copy, nullable) NSString *trailingSubcaption; //设置消息实体的图片 @property (nonatomic, strong, nullable) UIImage *image; //设置消息实体的媒体地址 须要注意 若是设置的image属性 这个属性将被忽略 @property (nonatomic, copy, nullable) NSURL *mediaFileURL; //设置消息实体的图标标题 @property (nonatomic, copy, nullable) NSString *imageTitle; //设置消息实体的图片子标题 @property (nonatomic, copy, nullable) NSString *imageSubtitle;
在制做表情包Sticker Picks的时候,开发者不须要编写一行代码,实际上若是要经过代码来开发表情包也是没有问题的,这里须要用到的一个类就是MSSticker类,简单理解,MSSticker类对象就是一个表情贴图,可是它不是一个View视图,若想在Messages App中看到这个表情贴图,还须要借助一个类MSStickerView,MSStickerView是用于承载表情贴图的视图类,用户选中它后,能够在Messages应用中进行发送。
首先,MSSticker类建立方法以下:
//初始化方法 经过文件URL 来建立实例 - (nullable instancetype)initWithContentsOfFileURL:(NSURL *)fileURL localizedDescription:(NSString *)localizedDescription error:(NSError * _Nullable *)error NS_DESIGNATED_INITIALIZER;
MSStickerView类解析以下:
//经过MSSticker来进行MSStickerView类的建立 - (instancetype)initWithFrame:(CGRect)frame sticker:(nullable MSSticker *)sticker; //获取动画播放一遍的时间 若是是gif @property(nonatomic, readonly) NSTimeInterval animationDuration; //开始动画 -(void) startAnimating; //结束动画 -(void) stopAnimating; //获取动画状态 - (BOOL)isAnimating;
须要注意,MSStickerView若是加载的是gif类型的表情贴图,默认不会播放动画,开发者能够调用开始动画的方法来进行gif动画的播放。
其实经过前面的内容,已经能够自定义开发一个表情包Messages App了,可是还有一个视图控制器类MSStickerBrowserViewController,这个类能够更加简单方面的建立表情包视图控制器。要了解MSStickerBrowserViewController类,首先应该先了解MSStickerBrowserView类,这两个类的关系十分相似于UITableViewController与UITableView类的关系。MSStickerBrowserView是用于展现表情视图的容器,其继承自UIView,但却和UICollectionView十分相似,其中方法解析以下:
//初始化方法 设置frame 和其中表情视图的尺寸模式 /* typedef NS_ENUM(NSInteger, MSStickerSize) { //小尺寸 MSStickerSizeSmall, //标准尺寸 MSStickerSizeRegular, //大尺寸 MSStickerSizeLarge } NS_ENUM_AVAILABLE_IOS(10_0); */ - (instancetype)initWithFrame:(CGRect)frame stickerSize:(MSStickerSize)stickerSize NS_DESIGNATED_INITIALIZER; //数据源代理 @property (nonatomic, weak, nullable) id <MSStickerBrowserViewDataSource> dataSource; //当前的滑动位置 @property (nonatomic, assign, readwrite) CGPoint contentOffset; //内容偏移尺寸 @property (nonatomic, assign, readwrite) UIEdgeInsets contentInset; //设置当前的滑动位置 - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated; //刷新数据 - (void)reloadData;
MSStickerBrowserView的数据填充须要在代理方法中实现,以下:
//设置表情贴图个数 - (NSInteger)numberOfStickersInStickerBrowserView:(MSStickerBrowserView *)stickerBrowserView; //设置具体每一个位置的表情贴图 - (MSSticker *)stickerBrowserView:(MSStickerBrowserView *)stickerBrowserView stickerAtIndex:(NSInteger)index;
再看MSStickerBrowserViewController就十分容易了,它只是将MSStickerBrowserView封装在了一个UIViewController中,而且这个UIViewController遵照了MSStickerBrowserViewDataSource协议,开发者直接实现协议方法便可。
下面是Apple对Messages App的定位和一些建议,还有个人一些理解:
1.确保应用是有用的而且易于理解。
2.功能要聚焦单一,不要组合多种功能在一块儿。
3.Messages一般用在双人非正式的交谈中,应从这里入手,让交流更加有趣。
4.Messages的最大两点是分享,利用这一点出发开发Messages App。
5.插图内容布局要注意,系统会自动将内容变为圆角,不要把重要的信息放在角落。
6.注意,在紧凑模式下,Messages App的界面是不容许水平滚动的。
7.一样,在紧凑模式下,Messages App不容许键盘输入。
专一技术,热爱生活,交流技术,也作朋友。
——珲少 QQ群:203317592