iOS10 iMessage 带来了你需要的MessageExtension

提示

前段时候因为一些原因,文章被删除了。现在再发一下,demo被删除了.......,有需要就我再码一个补上.....

项目要求

Xcode8 + iOS10
现在都是测试版,去开发者网站可以下载。如果没有的话,这个,那个,就看下我的配图吧,哈哈

前言

iOS10测试版已经出来一个多月了,一直对这个东西很感兴趣。但是查了好多,基本都是用Sticker的那个方面直接放图片使用的。到现在我是没有搜索到用代码编写的iMessage的项目,所以用了一天把这个项目写出来(前几天看过几次概念)。不理解的就去官方api文档看。现在是把苹果说的功能大概写了出来。有不对之处,请大家指出。

正文

在这里引用一下别人写的iMessage的解释,感觉解释的很好,我也直接引用了:

在iOS中新增了两种iMessage的方式,1.内置表情包,2.iMessage应用

1.内置表情包(Sticker Packs)

可以通过在Xcode中新建Sticker Pack Application来创建。这种方式可以简单地通过添加图片来在iMessage中添加表情包。添加的贴纸需要满足一下条件

图片类型必须是 png、apng、gif或者jpeg
文件大小必须 小于500K
图片大小必须在 100100 到 206206 之间
需要注意的是:必须要永远提供 @3x 大小的图片(即 300300 到 618618)。系统可以根据当前设备通过 runtime 自动调整图片来呈现 @2x 和 @1x
系统能够自适应的展示贴纸,所以为了更好的展示贴纸,最好提供的贴纸是以下三种大小的类型

小型 100*100
中型 136*136
大型 206*206

2.iMessage应用

iMessage app使用完整的框架和Message app进行交互。使用iMessage app能够

在消息应用内呈现一个自定义的用户交互界面。 使用 MSMessagesAppViewController
创建一个自定义或者动态的表情包浏览器。使用 MSStickerBrowserViewController
添加文本、表情、或者媒体文件到消息应用的文本输入框。使用 MSConversation
创建带有特定的应用数据交互的消息。使用 MSMessage
更新可以相互影响的消息(例如,创建游戏或者可以合作的应用)。使用 MSSession

内置表情包我就不在说了,我看了看网上有很好的例子也很简单。
MSMessagesAppViewController说白了就是显示表情的vc,和普通的vc一样,只是他多了一些方法,和AppDelegate的差不多。
我用了一个collectionview去显示图片,但是有一点需要注意,如果你直接用UIImageView去直接显示图片也是可以的,运行在message也是显示的,但是你却无法进行正常的拖拽,点击操作,因为你只是给了一个UIImageView而已。在这里要用到一个专门为显示iMessage里面图片用的类MSStickerView,他是UIView的一个子类,里面的属性很少

@interface MSStickerView : UIView

/*!
 @method   initWithFrame:sticker:
 @abstract   Initializes a MSStickerView with a frame and a MSSticker conforming object to display.
 */
- (instancetype)initWithFrame:(CGRect)frame sticker:(nullable MSSticker *)sticker;

/*!
 @property   sticker
 @abstract   The MSSticker object to display.
 @discussion Set this property to nil to remove the current sticker. Setting the
 sticker property does not change the size of a MSStickerView. Call sizeToFit to
 adjust the size of the view to match the sticker.
 */
@property (nonatomic, strong, readwrite, nullable) MSSticker *sticker;

/*!
 @property   animationDuration
 @abstract   The amount of time it takes to go through one cycle of the sticker animation.
 */
@property(nonatomic, readonly) NSTimeInterval animationDuration;

/*!
 @method    startAnimating
 @abstract  Starts animating the sticker in the receiver.
 @discussion This method always starts the animation from the first frame.
 */
-(void) startAnimating;

/*!
 @method    stopAnimating
 @abstract  Stops animating the sticker in the receiver.
 */
-(void) stopAnimating;

/*!
 @method   isAnimating
 @abstract   Returns a Boolean value indicating whether the animation is running.
 */
- (BOOL)isAnimating;

一目了然,sticker我的理解就是特殊的数据,让MSStickerView根据这个数据进行相应的显示操作。至于stopAnimating那几个方法我暂时没有用到。
UICollectionView代码块:

#import "StickerCollectionView.h"
#import "StickerCell.h"
#import "CommonCell.h"
static NSString * const STICKERCELL = @"StickerCell";
static NSString * const COMMONCELL = @"CommonCell";
@interface StickerCollectionView()

@property(nonatomic,strong)NSMutableArray *stickerArray;//数据
@end

@implementation StickerCollectionView
- (instancetype)initWithCoder:(NSCoder *)aDecoder{

   self = [super initWithCoder:aDecoder];
   if (self) {
       [self registerNib:[UINib nibWithNibName:STICKERCELL bundle:nil] forCellWithReuseIdentifier:STICKERCELL];//注册单元格
       [self registerNib:[UINib nibWithNibName:COMMONCELL bundle:nil] forCellWithReuseIdentifier:COMMONCELL];
   }
   return self;
}

- (void)awakeFromNib{

   [super awakeFromNib];
   self.delegate = self;
   self.dataSource = self;
   
}
//数据加载
- (NSMutableArray *)stickerArray{

   if (!_stickerArray) {
       _stickerArray = [NSMutableArray array];
       NSArray *dataArray = @[@"1",@"2",@"3",@"4",@"5",@"6",@"7"];
       
       //把图片转换为mssticker类的形式,用于显示
       [dataArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
           NSURL *urlStr = [[NSBundle mainBundle]URLForResource:obj withExtension:@"jpg"];
           MSSticker *placeSticker = [[MSSticker alloc]initWithContentsOfFileURL:urlStr localizedDescription:obj error:nil];
           [_stickerArray addObject:placeSticker];
       }];
   }
   return _stickerArray;
}


#pragma mark ------九宫格代理

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{

   return self.stickerArray.count + 4;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{

   if (indexPath.row >= self.stickerArray.count) {
       CommonCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:COMMONCELL forIndexPath:indexPath];
       cell.imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"other_%lu",indexPath.row - self.stickerArray.count + 1]];
       return cell;
   }else{
   
       StickerCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:STICKERCELL forIndexPath:indexPath];
       cell.sticker.sticker = self.stickerArray[indexPath.row];//把mssticker类型的数据赋予msstickerview里面的sticker属性
       return cell;
   }
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{

   if (indexPath.row == self.stickerArray.count) {
       //文字
       if (self.event1Block) {
           self.event1Block();
       }
   }else if (indexPath.row == self.stickerArray.count + 1){
   
       if (self.event2Block) {
           self.event2Block();
       }
       //音频
   }else if (indexPath.row == self.stickerArray.count + 2){
   
       if (self.event3Block) {
           self.event3Block();
       }
       //详细图
   }else if (indexPath.row == self.stickerArray.count + 3){
   
       if (self.event4Block) {
           self.event4Block();
       }
       //多图
   }
   
}

在这里面你会发现数据数组不再是简单的图片名字数据了,是一个MSSticker类型的数据。

- (nullable instancetype)initWithContentsOfFileURL:(NSURL *)fileURL localizedDescription:(NSString *)localizedDescription error:(NSError * _Nullable *)error NS_DESIGNATED_INITIALIZER;

/*!
 @property   imageFileURL
 @abstract   The file URL to the Sticker was initialized with.
 */
@property (nonatomic, strong, readonly) NSURL *imageFileURL;

/*!
 @property   localizedDescription
 @abstract   A succinct localized string describing the sticker.
 */
@property (nonatomic, copy, readonly) NSString *localizedDescription;

这个类里面的东西更加的少了,一个创建方法,一个NSURL,一个NSString。而且后面两个都是只读类型的,所以你基本不用操作。直接使用它唯一的方法就行了,localizedDescription要尽量设置的不同(官方好像说的是一个iMessage App里面的每一个MSSticker 的localizedDescription定义好了就不变了,除非卸载这个App.我解决的这个不是很好,英语太差)
把我们的基本数据转换成这个类之后然后传给MSStickerView类进行擦操作就行。

 cell.sticker.sticker = self.stickerArray[indexPath.row];//把mssticker类型的数据赋予msstickerview里面的sticker属性

至于他是怎么显示出来的,这个不用你担心。
显示效果如下:

效果一

然后点击发送:

效果二
效果三

这是基本的显示效果,iMessage还可以显示规定的文字,音频,详细的图片...
这些效果的实现是在MSMessagesAppViewController里面,MSMessagesAppViewController有个属性activeConversation,属于MSConversation类

/*!
 @method     insertMessage:localizedChangeDescription:completionHandler:
 @abstract   Stages provided the MSMessage for sending.
 @discussion This method inserts a MSMessage object into the Messages input field,
 Subsequent calls to this method will replace any existing message on the input field. 
 If the message was successfully inserted on the input field, the completion handler
 will be called with a nil error parameter otherwise the error parameter will be
 populated with an NSError object describing the failure.
 @param      message            The MSMessage instance describing the message to be sent.
 @param      completionHandler  A completion handler called when the message has been staged or if there was an error.
 */
- (void)insertMessage:(MSMessage *)message completionHandler:(nullable void (^)(NSError * _Nullable))completionHandler;

/*!
 @method     insertSticker:completionHandler:
 @abstract   The sticker is inserted into the Messages.app input field.
 @param      sticker            The sticker to be inserted.
 @param      completionHandler  A completion handler called when the insert is complete.
 */
- (void)insertSticker:(MSSticker *)sticker completionHandler:(nullable void (^)(NSError * _Nullable))completionHandler;

/*!
 @method     insertText:completionHandler:
 @abstract   The NSString instance provided in the text parameter is inserted into the Messages.app input field.
 @param      text               The text to be inserted.
 @param      completionHandler  A completion handler called when the insert is complete.
 */
- (void)insertText:(NSString *)text completionHandler:(nullable void (^)(NSError * _Nullable))completionHandler;

/*!
 @method     insertAttachment:withAlternateFilename:completionHandler:
 @abstract   The NSURL instance provided in the URL parameter is inserted into the Messages.app
 input field. This must be a file URL.
 @param      URL                The URL to the media file to be inserted.
 @param      filename           If you supply a string here, the message UI uses it for the attachment. Use an alternate filename to better describe the attachment or to make the name more readable.
 @param      completionHandler  A completion handler called when the insert is complete.
 */
- (void)insertAttachment:(NSURL *)URL withAlternateFilename:(nullable NSString *)filename completionHandler:(nullable void (^)(NSError * _Nullable))completionHandler;

这是MSConversation类的四个方法,也是他的全部方法,用于插入各种对用的内容。

文字

self.collectionView.event1Block = ^{
    
        //发送文字
        [weakSelf.activeConversation insertText:@"Fuck the world if you are rich,otherwise fuck youself!" completionHandler:^(NSError * _Nullable error) {
            //使用的时候的回调,做自己想做的事情
        }];

效果:

文字

音频

 //发送音频
        NSString *path = [[NSBundle mainBundle]pathForResource:@"music" ofType:@"mp3"];
        NSURL *url = [NSURL fileURLWithPath:path];
        [self.activeConversation insertAttachment:url withAlternateFilename:@"音乐" completionHandler:^(NSError * _Nullable error) {
            //使用的时候的回调,做自己想做的事情
        }];

效果:

音频
音频

详细的MSMessage


        //详细图片
        MSMessage *message = [[MSMessage alloc]init];
        MSMessageTemplateLayout *layout = [[MSMessageTemplateLayout alloc]init];
        layout.imageTitle = @"蜡笔";
        layout.imageSubtitle = @"蜡笔小新的故事";
        layout.caption = @"蜡笔";
        layout.subcaption = @"蜡笔sub";
        layout.trailingCaption = @"蜡笔";
        layout.trailingSubcaption = @"蜡笔sub";
        NSString *path2 = [[NSBundle mainBundle]pathForResource:@"1" ofType:@"jpg"];
        layout.mediaFileURL = [NSURL fileURLWithPath:path2];
        message.layout = layout;
        [self.activeConversation insertMessage:message completionHandler:^(NSError * error) {
            //使用的时候的回调,做自己想做的事情
        }];

效果:

详细图片

这是我用UICollectionView实现的布局,其实iMessage也给了一个类似的可以实现的view和vc:MSStickerBrowserView,MSStickerBrowserViewController

@interface MSStickerBrowserViewController : UIViewController <MSStickerBrowserViewDataSource>

/*!
 @method   initWithStickerSize:
 @abstract Initializes a MSStickerBrowserViewController and configures it's MSStickerBrowserView with the provided sticker size class.
 */
- (instancetype)initWithStickerSize:(MSStickerSize)stickerSize NS_DESIGNATED_INITIALIZER;

/*!
 @property   stickerBrowserView
 @abstract   Returns the sticker browser view managed by the controller object.
 */
@property (nonatomic, strong, readonly) MSStickerBrowserView *stickerBrowserView;

/*!
 * @abstract Controls the size of the stickers are displayed at in the sticker browser view.
 */
@property (nonatomic, readonly) MSStickerSize stickerSize;

MSStickerBrowserViewController里面就这一点东西,一个构造,两个只读属性。
要创建MSStickerBrowserViewController一定要实现代理的两个方法,与表很像的两个代理:

/*!
 * @abstract Returns the number of Stickers that the sticker browser should show.
 * @param stickerBrowserView The sticker browser view .
 * @result The number of stickers.
 */
- (NSInteger)numberOfStickersInStickerBrowserView:(MSStickerBrowserView *)stickerBrowserView;

/*!
 * @abstract Returns the sticker that the sticker browser should show in the browser.
 * @param stickerBrowserView The sticker browser view.
 * @param index The index of the sticker to show.
 * @result A MSSticker object.
 */
- (MSSticker *)stickerBrowserView:(MSStickerBrowserView *)stickerBrowserView stickerAtIndex:(NSInteger)index;

就是个数和显示的内容,我实现的是两行显示,我不知道可以可以改变这个:

//数据加载
- (NSMutableArray *)browArray{
    
    if (!_browArray) {
        _browArray = [NSMutableArray array];
        NSArray *dataArray = @[@"dt_1",@"dt_2",@"dt_3",@"dt_4",@"dt_5",@"dt_6",@"dt_7",@"dt_8",@"dt_9",@"dt_10",@"dt_11",@"dt_12",@"dt_13"];
        
        //把图片转换为mssticker类的形式,用于显示
        [dataArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            NSURL *urlStr = [[NSBundle mainBundle]URLForResource:obj withExtension:@"gif"];
            MSSticker *placeSticker = [[MSSticker alloc]initWithContentsOfFileURL:urlStr localizedDescription:obj error:nil];
            [_browArray addObject:placeSticker];
        }];
    }
    return _browArray;
}

#pragma mark----代理

- (NSInteger)numberOfStickersInStickerBrowserView:(MSStickerBrowserView *)stickerBrowserView{
    
    return self.browArray.count;
}
- (MSSticker *)stickerBrowserView:(MSStickerBrowserView *)stickerBrowserView stickerAtIndex:(NSInteger)index{
    
    return self.browArray[index];
}

效果:

效果

结语

基本说完了,我没有详细的介绍每个类用法,里面什么属性方法,这个我感觉放到demo你一跑起来看下代码就应该明白。其实iMessage很简单,我写的demo只是简单的把它基本使用了,还有好多等待你的开发。如果你感觉对你有一点帮助,请给个github星星,谢了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容