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