• Stars
    star
    154
  • Rank 242,095 (Top 5 %)
  • Language
    Objective-C
  • Created over 9 years ago
  • Updated over 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

This is a simple demo for those who want to make apps chatting on apple watch

#4月6日更新 真机测试结果 非常抱歉的告诉大家看,本Demo关于录音和播放的环节全部不能在真机上完美运行,AVAudio的代码在模拟器和真机上的实现效果差异很大,语音播放会通过iPhone,录制则结果是空的文件微信一样没办法做,应该是没有开放扬声器和麦克风的接口

#效果 image

#截图 imageimageimageimageimage

#1-3. 都是废话,删了 #4. Start App结构:

1. 消息列表 InterdaceController(IC) 用来展示未读消息总览
2. 聊天页面 ChatController(CC) 用来聊天
3. 语音录制 RecordController(RC) 用来录制语音

##4.1 消息列表 单一样式的Table就可以搞定,并没有难度。把Table元素加入到IC,并创引用, 我需要展示用户的头像,名称,未读消息数,那么每个Table Row需要一个Image,两个Label。如果你想要一个圆角的头像,那么一个Image就满足不了你,你需要在Image外面套一层Group,通过Group来实现圆角。

为了获取到这几个UI元素的Reference,我们不得不创建一个基于NSObject的子类,且叫ChatRowController(CRC). 并在SB把Table Row Controller的类改为ChatRowController。需要注意都是,你需要在CRC中 #include <WatchKit/WatchKit.h>

每一个Table Row Controller都有一个RowType,在SB中叫做Identifier,这个值是Table在创建每个Row的时候必须要用到的

WApp中不存在类似UITableViewCell复用的情况,所以代码上可以省略很多不必要的考虑,但是性能上,你不可以一次性加载太多TableRow!可以分批按需加载。

###第一个问题来了 头像怎么加载? WKInterfaceController 下有个方法是

+ (BOOL)openParentApplication:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo, NSError *error)) reply;

为了方便,我们可以给WKInterfaceImage加一个Category,加入方法

- (void)loadImageWithURLString:(NSString *)urlString placeholder:(UIImage *)image
{
	[self setImage:image];
	if (!urlString) {
    	return;
	}
    [WKInterfaceController openParentApplication:@{@"key":@"loadImage", @"urlString":urlString} reply:^(NSDictionary *replyInfo, NSError *error) {
    	NSData *data = replyInfo[@"result"];
        [self setImageData:data];
	}];
}

同时到AppDelegate下重载,这里使用的是SDImageCache来管理图片缓存

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {
	NSString *value = userInfo[@"key"];
    if ([value isEqualToString:@"loadImage"]) {
	    NSURL *url = [NSURL URLWithString:userInfo[@"urlString"]];
        UIImage *image = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:url.absoluteString];
	    NSData *data = nil;
    	BOOL cached = YES;
        if (!image) {
	        cached = NO;
    	    data = [NSData dataWithContentsOfURL:url];
        	image = [UIImage imageWithData:data];
            [[SDImageCache sharedImageCache] storeImage:image forKey:url.absoluteString toDisk:YES];
	    } else {
    	    data = UIImageJPEGRepresentation(image, 1);
        }
	    if (data) {
    	    reply(@{@"result":data, @"isFromCache":@(cached)});
        }
    }
}

在Category的帮助下,我们只需要设定URLString就可以了

[crc.avatarImage loadImageWithURLString:avatar placeholder:nil];

##4.2 聊天页面 我们需要支持展示聊天内容,播放聊天语音

###第二个问题来了 我的消息发出和收到,一个是靠左,一个是靠右对齐的,单一的TableRowController无法实现 我一度为这个问题难倒,创建了非常复杂的RowController,企图通过设置多个透明的Group来帮助聊天消息的对齐走向,我太天真了。 可是微信已经实现了啊!难道他有特殊方法吗。。呵呵,其实是我的思维没打开,还是固定在iOS App上的开发方式,一个Table 用一类Cell解决所有UI。但是其实一个Table可以有多个RowController。当然其实这些RowController其实结构都一样,只不过部分元素在细节上不同,但代码是无法控制的!只能通过SB来创建不同的RowController,通过定义不同的Identifier来实现。但是这些RowController其实都可以是一个类。

- (void)setupTable
{
	_messages = [NSMutableArray array];
    for (int i = 0; i < rand()%20; i++) {
	    [_messages addObject:@{@"msg":@[@"Hi", @"OK", @"Nice to meet you", @"Fine"][rand()%4], @"source":@(rand()%2), @"type":@(rand()%3)}];
    }

	//先清空预置的row
    [_table removeRowsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, _table.numberOfRows)]];
	for (int i = 0; i < _messages.count; i++) {
    	NSDictionary *messageDic = _messages[i];
        [_table insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:i] withRowType:[self rowTypeForMessage:messageDic]];
	    MessageRowController *mrc = [_table rowControllerAtIndex:i];
        if ([messageDic[@"type"] intValue] == MessageTypeText) {
	        [mrc.label setText:messageDic[@"msg"]];
    	} else if ([messageDic[@"type"] intValue] == MessageTypeVoice) {
        	
        } else {
	        [mrc.image loadImageWithURLString:@"http://tp3.sinaimg.cn/1657938842/180/5704612869/1" 	placeholder:nil];
	    }
    }
}

- (NSString *)rowTypeForMessage:(NSDictionary *)messageDic
{
	NSMutableString *rowType = [NSMutableString string];
    if ([messageDic[@"source"] intValue] == MessageSourceIncoming) {
	    [rowType appendString:@"Incoming"];
    } else {
	    [rowType appendString:@"Outgoing"];
    }

	if ([messageDic[@"type"] intValue] == MessageTypeText) {
        [rowType appendString:@"Text"];
	} else if ([messageDic[@"type"] intValue] == MessageTypeVoice) {
        [rowType appendString:@"Voice"];
    } else {
	    [rowType appendString:@"Image"];
    }
	return rowType;
}

###第三个问题来了 语音怎么播放? 首先你需要#import <AVFoundation/AVFoundation.h>然后就简单啦

- (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex
{
	NSDictionary *messageDic = _messages[rowIndex];
    if ([messageDic[@"type"] intValue] == MessageTypeVoice)
	{
    	NSError *error = nil;
        // 如果是网络语音,你需要下载语音,和图片一样,通过OpenParent下载好后传回来就行了,这里不多写了
	    NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"send" 	ofType:@"mp3"]];
	    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:data error:&error];
    	player.delegate = self;
        if (error) {
	        NSLog(@"%@", error);
    	    return;
        }
	    [_player stop];
    	_player = nil;
        [player play];
	    _player = player;
    }
}

网络语音和网络图片一样,都通过OpenParent来达到目的,具体缓存什么的都去AppDelegate里面写吧

###第四个问题来了 怎么文字回复? WatchKit里有一个输入法,任何一个WKInterfaceController都可以调用

- (void)presentTextInputControllerWithSuggestions:(NSArray *)suggestions allowedInputMode:(WKTextInputMode)inputMode completion:(void(^)(NSArray *results))completion; // results is nil if cancelled

至于用户怎么输入就不用管啦~

##4.3 录音页面 ###唯一问题怎么录音 同样,引入#import <AVFoundation/AVFoundation.h>然后就简单啦

_recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:&err];
[_recorder record];

其实都和iPhone里一样~

#5. 总结以及其他坑 ##5.1 当Controller不在可见屏幕内的时候,不要更新UI,因为会失败,等willActive后再更新UI

##5.2 把需要大量计算的工作交给iPhone,比如我需要将PCM转换为MP3,我需要下载图片,我需要缓存语音等等

##5.3 如果有两张图,一张叫ring1 一张叫ring10 当你尝试把使用[image setImageNamed:@"ring1"]的时候,出来的会是ring10!

##5.4 Apple Watch App 的功能点一定要简单和集中,不要试图把整个iOS App搬过来,苹果也不会喜欢太复杂的

##5.5 用更多的图片代替文字 让WApp显得高大上

##5.6 资源:圆环指示器快速生成

##5.7 Demo源码:GitHub

##5.8 App主动与WApp沟通