Initial commit
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// RTSDemoViewController.h
|
||||
// NIM
|
||||
//
|
||||
// Created by 高峰 on 15/7/1.
|
||||
// Copyright (c) 2015年 Netease. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface NTESWhiteboardViewController : UIViewController
|
||||
|
||||
- (id)initWithSessionID:(NSString *)sessionID
|
||||
peerID:(NSString *)peerID
|
||||
types:(NSUInteger)types
|
||||
info:(NSString *)info;
|
||||
|
||||
|
||||
@end
|
||||
+668
@@ -0,0 +1,668 @@
|
||||
//
|
||||
// RTSDemoViewController.m
|
||||
// NIM
|
||||
//
|
||||
// Created by 高峰 on 15/7/1.
|
||||
// Copyright (c) 2015年 Netease. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NTESWhiteboardViewController.h"
|
||||
#import <NIMSDK/NIMSDK.h>
|
||||
#import "NTESWhiteboardDrawView.h"
|
||||
#import "NTESTimerHolder.h"
|
||||
#import "Toast+UIView.h"
|
||||
#import "UIActionSheet+NTESBlock.h"
|
||||
#import "NIMAvatarImageView.h"
|
||||
#import "NTESWhiteboardAttachment.h"
|
||||
#import "NTESSessionMsgConverter.h"
|
||||
//#import "NIMRTSRecordingInfo.h"
|
||||
#import "NTESDevice.h"
|
||||
#import "NTESBundleSetting.h"
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WhiteBoardCmdType){
|
||||
WhiteBoardCmdTypePointStart = 1,
|
||||
WhiteBoardCmdTypePointMove = 2,
|
||||
WhiteBoardCmdTypePointEnd = 3,
|
||||
|
||||
WhiteBoardCmdTypeCancelLine = 4,
|
||||
WhiteBoardCmdTypePacketID = 5,
|
||||
WhiteBoardCmdTypeClearLines = 6,
|
||||
WhiteBoardCmdTypeClearLinesAck = 7,
|
||||
};
|
||||
|
||||
static const NSTimeInterval CallerWaitSeconds = 40;
|
||||
static const NSTimeInterval SendCmdIntervalSeconds = 0.06;
|
||||
|
||||
@interface NTESWhiteboardViewController ()<NIMRTSManagerDelegate, NTESTimerHolderDelegate>
|
||||
|
||||
@property (strong, nonatomic) NTESWhiteboardDrawView *myDrawView;
|
||||
@property (strong, nonatomic) NTESWhiteboardDrawView *peerDrawView;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *acceptButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *rejectButton;
|
||||
@property (weak, nonatomic) IBOutlet UIView *headerView;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *footerView;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *muteButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *SpeakerButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *cancelButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *clearButton;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *hintTextLabel;
|
||||
@property (weak, nonatomic) IBOutlet NIMAvatarImageView *avatarImageView;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *nameTextLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *closeButton;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *closeLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *cancelRequestButton;
|
||||
|
||||
@property (copy, nonatomic) NSString *sessionID;
|
||||
@property (copy, nonatomic) NSString *peerID;
|
||||
@property (assign, nonatomic) NSUInteger types;
|
||||
@property (copy, nonatomic) NSString *info;
|
||||
@property (assign, nonatomic) BOOL isCaller;
|
||||
|
||||
@property (assign, nonatomic) BOOL mute;
|
||||
@property (assign, nonatomic) BOOL speaker;
|
||||
|
||||
@property (strong, nonatomic) NSMutableString *cmds;
|
||||
@property (strong, nonatomic) NSLock *cmdsLock;
|
||||
@property (strong, nonatomic) NTESTimerHolder *sendCmdsTimer;
|
||||
@property (assign, nonatomic) NSUInteger drawViewWidth;
|
||||
|
||||
@property (strong, nonatomic) NTESTimerHolder *callerWaitingTimer;
|
||||
|
||||
@property (assign, nonatomic) UInt64 refPacketID;
|
||||
|
||||
@property (assign, nonatomic) BOOL audioConnected;
|
||||
@property (assign, nonatomic) BOOL dismissed;
|
||||
|
||||
@property (assign, nonatomic) BOOL needTerminateRTS;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NTESWhiteboardViewController
|
||||
|
||||
#pragma mark - public methods
|
||||
- (id)initWithSessionID:(NSString *)sessionID
|
||||
peerID:(NSString *)peerID
|
||||
types:(NSUInteger)types
|
||||
info:(NSString *)info
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_sessionID = sessionID;
|
||||
_peerID = peerID;
|
||||
_types = types;
|
||||
_info = info;
|
||||
_isCaller = _sessionID ? NO : YES;
|
||||
_mute = YES;
|
||||
_speaker = YES;
|
||||
_cmds = [[NSMutableString alloc] initWithCapacity:1];
|
||||
_cmdsLock = [[NSLock alloc] init];
|
||||
_sendCmdsTimer = [[NTESTimerHolder alloc] init];
|
||||
_needTerminateRTS = YES;
|
||||
}
|
||||
return self;
|
||||
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
id<NIMRTSManager> manager = [NIMAVChatSDK sharedSDK].rtsManager;
|
||||
[manager addDelegate:self];
|
||||
[manager addDelegate:self];
|
||||
[self updateButton];
|
||||
|
||||
NIMKitInfo *info = [[NIMKit sharedKit] infoByUser:_peerID];
|
||||
NSURL *avatarURL;
|
||||
if (info.avatarUrlString.length) {
|
||||
avatarURL = [NSURL URLWithString:info.avatarUrlString];
|
||||
}
|
||||
[_avatarImageView nim_setImageWithURL:avatarURL placeholderImage:info.avatarImage options:SDWebImageRetryFailed];
|
||||
|
||||
[_nameTextLabel setText:[info showName]];
|
||||
|
||||
self.view.backgroundColor = UIColorFromRGB(0xE9ECF0);
|
||||
|
||||
[_nameTextLabel setTextColor:UIColorFromRGB(0x868686)];
|
||||
[_hintTextLabel setTextColor:UIColorFromRGB(0x868686)];
|
||||
|
||||
[_cancelRequestButton setBackgroundColor:UIColorFromRGB(0xF54A63)];
|
||||
[_rejectButton setBackgroundColor:UIColorFromRGB(0xF54A63)];
|
||||
[_acceptButton setBackgroundColor:UIColorFromRGB(0x0F91F2)];
|
||||
[_cancelButton setBackgroundColor:UIColorFromRGB(0x0F91F2)];
|
||||
[_clearButton setBackgroundColor:UIColorFromRGB(0xF54A63)];
|
||||
[_headerView setBackgroundColor:UIColorFromRGB(0xFDFDFE)];
|
||||
|
||||
_rejectButton.layer.cornerRadius = 8;
|
||||
_acceptButton.layer.cornerRadius = 8;
|
||||
_cancelRequestButton.layer.cornerRadius = 8;
|
||||
_cancelButton.layer.cornerRadius = 5;
|
||||
_clearButton.layer.cornerRadius = 5;
|
||||
|
||||
_headerView.layer.borderWidth = 0.5;
|
||||
_headerView.layer.borderColor = UIColorFromRGB(0x9FA1A5).CGColor;
|
||||
_footerView.layer.borderWidth = 0.5;
|
||||
_footerView.layer.borderColor = UIColorFromRGB(0x9FA1A5).CGColor;
|
||||
|
||||
if (_isCaller) {
|
||||
[self switchToCallerView];
|
||||
[self requestRTS];
|
||||
[_hintTextLabel setText:@"正在邀请对方, 请稍后"];
|
||||
_callerWaitingTimer = [[NTESTimerHolder alloc] init];
|
||||
[_callerWaitingTimer startTimer:CallerWaitSeconds delegate:self repeats:NO];
|
||||
}
|
||||
else {
|
||||
[_hintTextLabel setText:@"对方发起白板演示"];
|
||||
[self switchToCalleeView];
|
||||
}
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:animated];
|
||||
}
|
||||
- (void)dealloc
|
||||
{
|
||||
[self termimateRTS];
|
||||
}
|
||||
|
||||
#pragma mark - user interfaces
|
||||
- (IBAction)onRejectButtonPressed:(id)sender {
|
||||
[self responseRTS:NO];
|
||||
[self dismiss];
|
||||
}
|
||||
- (IBAction)onAcceptButtonPressed:(id)sender {
|
||||
|
||||
if (_types & NIMRTSServiceAudio) {
|
||||
UInt64 currentNetcall = [[NIMAVChatSDK sharedSDK].netCallManager currentCallID];
|
||||
if (currentNetcall) {
|
||||
[[NIMAVChatSDK sharedSDK].netCallManager hangup:currentNetcall];
|
||||
}
|
||||
}
|
||||
|
||||
[self responseRTS:YES];
|
||||
[self switchToWaitingConnectView];
|
||||
}
|
||||
- (IBAction)onMuteButtonPressed:(id)sender {
|
||||
_mute = !_mute;
|
||||
[self updateButton];
|
||||
[[NIMAVChatSDK sharedSDK].rtsManager setMute:_mute];
|
||||
[[NIMAVChatSDK sharedSDK].rtsManager sendRTSControl:_mute ? @"关闭了声音" : @"打开了声音" forSession:_sessionID];
|
||||
}
|
||||
- (IBAction)onSpeakerButtonPressed:(id)sender {
|
||||
_speaker = !_speaker;
|
||||
[self updateButton];
|
||||
[[NIMAVChatSDK sharedSDK].rtsManager setSpeaker:_speaker];
|
||||
}
|
||||
- (IBAction)onCloseButtonPressed:(id)sender {
|
||||
|
||||
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"退出后, 你将不再接收白板演示的消息内容" delegate:nil cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"退出", nil];
|
||||
__weak typeof(self) wself = self;
|
||||
[sheet showInView:self.view completionHandler:^(NSInteger index) {
|
||||
if (index != sheet.cancelButtonIndex) {
|
||||
[wself termimateRTS];
|
||||
[wself dismissAfter:2];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)onCancelButtonPressed:(id)sender {
|
||||
[_myDrawView deleteLastLine];
|
||||
[self sendWhiteboardCmd:WhiteBoardCmdTypeCancelLine];
|
||||
}
|
||||
|
||||
- (IBAction)onClearLinesButtonPressed:(id)sender {
|
||||
[self clearWhiteboard];
|
||||
[self sendWhiteboardCmd:WhiteBoardCmdTypeClearLines];
|
||||
}
|
||||
|
||||
- (IBAction)onCancelRequestButtonPressed:(id)sender
|
||||
{
|
||||
[self termimateRTS];
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - delegates
|
||||
#pragma mark NIMRTSManagerDelegate
|
||||
- (void)onRTSResponse:(NSString *)sessionID
|
||||
from:(NSString *)callee
|
||||
accepted:(BOOL)accepted
|
||||
{
|
||||
if (!accepted) {
|
||||
NSLog(@"RTSDemo: peer rejected");
|
||||
[self makeToast:@"对方拒绝了本次请求"];
|
||||
[self dismiss];
|
||||
}
|
||||
else {
|
||||
[self switchToWaitingConnectView];
|
||||
[_callerWaitingTimer stopTimer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRTSTerminate:(NSString *)sessionID
|
||||
by:(NSString *)user
|
||||
{
|
||||
NSLog(@"RTSDemo: peer terminated, session id %@, current session id %@", sessionID, _sessionID);
|
||||
if (sessionID == _sessionID) {
|
||||
[self makeToast:@"对方已离开"];
|
||||
[self termimateRTS];
|
||||
if ([[NTESDevice currentDevice] isInBackground]) {
|
||||
[self dismiss];
|
||||
}
|
||||
else {
|
||||
[self dismissAfter:2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRTSResponsedByOther:(NSString *)sessionID
|
||||
accepted:(BOOL)accepted
|
||||
{
|
||||
NSLog(@"RTSDemo: responsed by other");
|
||||
[self makeToast:[NSString stringWithFormat:@"已在其他端%@", accepted ? @"接受" : @"拒绝"]];
|
||||
_needTerminateRTS = NO;
|
||||
[self dismissAfter:2];
|
||||
|
||||
|
||||
}
|
||||
- (void)onRTS:(NSString *)sessionID
|
||||
service:(NIMRTSService)type
|
||||
status:(NIMRTSStatus)status
|
||||
error:(NSError *)error
|
||||
{
|
||||
NSLog(@"RTSDemo: service %zd status %zd", type, status);
|
||||
if (type == NIMRTSServiceReliableTransfer) {
|
||||
if (status == NIMRTSStatusConnect) {
|
||||
[self switchToConnectedView];
|
||||
[_sendCmdsTimer startTimer:SendCmdIntervalSeconds delegate:self repeats:YES];
|
||||
}
|
||||
else {
|
||||
NSLog(@"已断开连接: %zd", error.code);
|
||||
[self termimateRTS];
|
||||
[self dismissAfter:1];
|
||||
}
|
||||
}
|
||||
else if (type == NIMRTSServiceAudio) {
|
||||
_audioConnected = (status == NIMRTSStatusConnect) ? YES : NO;
|
||||
if (_audioConnected) {
|
||||
[[NIMAVChatSDK sharedSDK].rtsManager setMute:_mute];
|
||||
}
|
||||
else {
|
||||
NSLog(@"已断开音频服务: %zd", error.code);
|
||||
}
|
||||
if (![_footerView isHidden]) {
|
||||
[_muteButton setHidden:!_audioConnected];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRTSReceive:(NSString *)sessionID
|
||||
data:(NSData *)data
|
||||
from:(NSString *)user
|
||||
withIn:(NIMRTSService)channel
|
||||
{
|
||||
NSString *cmdString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
// DDLogDebug(@"receive app data:%@", cmdString);
|
||||
NSArray *cmds = [cmdString componentsSeparatedByString:@";"];
|
||||
BOOL newLine = NO;
|
||||
NSMutableArray *points = [[NSMutableArray alloc] init];
|
||||
for (NSString *cmdString in cmds) {
|
||||
if ([cmdString rangeOfString:@":"].length == 0) {
|
||||
continue;
|
||||
}
|
||||
NSArray *cmd = [cmdString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@":,"]];
|
||||
NSAssert(cmd.count == 3, @"Invalid cmd");
|
||||
|
||||
NSInteger c = [cmd[0] integerValue];
|
||||
NSArray *point = [NSArray arrayWithObjects:
|
||||
@([cmd[1] floatValue] * _drawViewWidth),
|
||||
@([cmd[2] floatValue] * _drawViewWidth), nil];
|
||||
switch (c) {
|
||||
case WhiteBoardCmdTypePointStart:
|
||||
if ([points count] > 0) {
|
||||
[_peerDrawView addPoints:points isNewLine:newLine];
|
||||
points = [[NSMutableArray alloc] init];
|
||||
}
|
||||
newLine = YES;
|
||||
case WhiteBoardCmdTypePointMove:
|
||||
case WhiteBoardCmdTypePointEnd:
|
||||
[points addObject:point];
|
||||
break;
|
||||
case WhiteBoardCmdTypeCancelLine:
|
||||
[_peerDrawView deleteLastLine];
|
||||
break;
|
||||
// case WhiteBoardCmdTypePacketID:
|
||||
// DDLogDebug(@"------receive cmd id %@", cmd[1]);
|
||||
// break;
|
||||
case WhiteBoardCmdTypeClearLines:
|
||||
[self clearWhiteboard];
|
||||
[self sendWhiteboardCmd:WhiteBoardCmdTypeClearLinesAck];
|
||||
break;
|
||||
case WhiteBoardCmdTypeClearLinesAck:
|
||||
[self clearWhiteboard];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ([points count] > 0) {
|
||||
[_peerDrawView addPoints:points isNewLine:newLine];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRTSControl:(NSString *)controlInfo
|
||||
from:(NSString *)user
|
||||
forSession:(NSString *)sessionID
|
||||
{
|
||||
if (sessionID == _sessionID) {
|
||||
NSLog(@"Receive control info [%@] from %@", controlInfo, user);
|
||||
[self.view.window makeToast:[NSString stringWithFormat:@"%@: %@", user, controlInfo]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRTSRecordingInfo:(NIMRTSRecordingInfo *)info
|
||||
forSession:(NSString *)sessionID
|
||||
{
|
||||
// NSString *fileType = (info.service == NIMRTSServiceReliableTransfer) ? @"白板" : @"语音";
|
||||
// [self sendMessage:[NTESSessionMsgConverter msgWithText:[NSString stringWithFormat:@"%@:%@", fileType, info.recordFileName]]];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark UIResponder
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
CGPoint p = [[touches anyObject] locationInView:_myDrawView];
|
||||
[self onPointCollected:p type:WhiteBoardCmdTypePointStart];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
CGPoint p = [[touches anyObject] locationInView:_myDrawView];
|
||||
[self onPointCollected:p type:WhiteBoardCmdTypePointMove];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
CGPoint p = [[touches anyObject] locationInView:_myDrawView];
|
||||
[self onPointCollected:p type:WhiteBoardCmdTypePointEnd];
|
||||
}
|
||||
|
||||
#pragma mark M80TimerHolderDelegate
|
||||
- (void)onNTESTimerFired:(NTESTimerHolder *)holder
|
||||
{
|
||||
if (holder == _sendCmdsTimer) {
|
||||
[self sendCmds];
|
||||
}
|
||||
else if (holder == _callerWaitingTimer) {
|
||||
[self makeToast:@"对方未接受请求"];
|
||||
[self termimateRTS];
|
||||
[self dismissAfter:2];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - private methods
|
||||
- (void)requestRTS
|
||||
{
|
||||
|
||||
NIMRTSOption *option = [[NIMRTSOption alloc] init];
|
||||
|
||||
option.extendMessage = @"白板请求扩展信息";
|
||||
option.apnsContent = @"邀请你加入白板会话";
|
||||
option.apnsSound = @"video_chat_tip_receiver.aac";
|
||||
[self fillUserSetting:option];
|
||||
|
||||
__weak typeof(self) wself = self;
|
||||
_sessionID = [[NIMAVChatSDK sharedSDK].rtsManager requestRTS:[NSArray arrayWithObject:_peerID]
|
||||
services:_types
|
||||
option:option
|
||||
completion:^(NSError *error, NSString *sessionID, UInt64 channelID)
|
||||
{
|
||||
if (error) {
|
||||
NSLog(@"RTSDemo: send request failed %zd!!!", error.code);
|
||||
NSString *errToast = (error.code == NIMRemoteErrorCodeCalleeOffline) ? @"对方不在线" : [NSString stringWithFormat:@"发起失败:%zd", error.code];
|
||||
[wself makeToast:errToast];
|
||||
[wself dismissAfter:2];
|
||||
}
|
||||
else {
|
||||
|
||||
NTESWhiteboardAttachment *attachment = [[NTESWhiteboardAttachment alloc] init];
|
||||
attachment.flag = CustomWhiteboardFlagInvite;
|
||||
NIMMessage *message = [NTESSessionMsgConverter msgWithWhiteboardAttachment:attachment];
|
||||
[wself sendMessage:message];
|
||||
}
|
||||
}];
|
||||
}
|
||||
- (void)responseRTS:(BOOL)accepted
|
||||
{
|
||||
NIMRTSOption *option = [[NIMRTSOption alloc] init];
|
||||
[self fillUserSetting:option];
|
||||
|
||||
__weak typeof(self) wself = self;
|
||||
[[NIMAVChatSDK sharedSDK].rtsManager responseRTS:_sessionID
|
||||
accept:accepted
|
||||
option:option
|
||||
completion:^(NSError *error, NSString *sessionID, UInt64 channelID) {
|
||||
if (error) {
|
||||
[wself makeToast:[NSString stringWithFormat:@"接听失败:%zd", error.code]];
|
||||
|
||||
[wself dismissAfter:2];
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)termimateRTS
|
||||
{
|
||||
if (_needTerminateRTS) {
|
||||
_needTerminateRTS = NO;
|
||||
[[NIMAVChatSDK sharedSDK].rtsManager terminateRTS:_sessionID];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onPointCollected:(CGPoint)p type:(WhiteBoardCmdType)type
|
||||
{
|
||||
//send to peer
|
||||
NSString *cmd = [NSString stringWithFormat:@"%zd:%.3f,%.3f;", type, p.x/_drawViewWidth, p.y/_drawViewWidth];
|
||||
[self addCmd:cmd];
|
||||
|
||||
//local render
|
||||
NSArray *point = [NSArray arrayWithObjects:@(p.x), @(p.y), nil];
|
||||
[_myDrawView addPoints:[NSMutableArray arrayWithObjects:point, nil]
|
||||
isNewLine:(type == WhiteBoardCmdTypePointStart)];
|
||||
}
|
||||
|
||||
- (void)addCmd:(NSString *)aCmd
|
||||
{
|
||||
[_cmdsLock lock];
|
||||
[_cmds appendString:aCmd];
|
||||
[_cmdsLock unlock];
|
||||
|
||||
if ([_cmds length] >= 30000) {
|
||||
[self sendCmds];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendCmds
|
||||
{
|
||||
[_cmdsLock lock];
|
||||
if ([_cmds length] > 0) {
|
||||
// DDLogDebug(@"++++++send cmd id %llu", _refPacketID);
|
||||
NSString *cmd = [NSString stringWithFormat:@"%zd:%llu,0;", WhiteBoardCmdTypePacketID, _refPacketID ++];
|
||||
[_cmds appendString:cmd];
|
||||
|
||||
[self sendRTSData:_cmds];
|
||||
[_cmds setString:@""];
|
||||
}
|
||||
[_cmdsLock unlock];
|
||||
}
|
||||
|
||||
|
||||
- (void)sendRTSData:(NSString *)data
|
||||
{
|
||||
BOOL success = [[NIMAVChatSDK sharedSDK].rtsManager sendRTSData:[data dataUsingEncoding:NSUTF8StringEncoding]
|
||||
from:_sessionID
|
||||
to:(_isCaller ? nil : _peerID) //单播和广播发送示例
|
||||
with:NIMRTSServiceReliableTransfer];
|
||||
if (!success) {
|
||||
[self.view.window makeToast:@"数据发送失败"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)switchToCallerView
|
||||
{
|
||||
[_rejectButton setHidden:YES];
|
||||
[_acceptButton setHidden:YES];
|
||||
[_cancelRequestButton setHidden:NO];
|
||||
[_footerView setHidden:YES];
|
||||
[_hintTextLabel setHidden:NO];
|
||||
[_avatarImageView setHidden:NO];
|
||||
[_nameTextLabel setHidden:NO];
|
||||
[_closeButton setHidden:YES];
|
||||
[_closeLabel setHidden:YES];
|
||||
}
|
||||
|
||||
|
||||
- (void)switchToCalleeView
|
||||
{
|
||||
[_rejectButton setHidden:NO];
|
||||
[_acceptButton setHidden:NO];
|
||||
[_cancelRequestButton setHidden:YES];
|
||||
[_footerView setHidden:YES];
|
||||
[_hintTextLabel setHidden:NO];
|
||||
[_avatarImageView setHidden:NO];
|
||||
[_nameTextLabel setHidden:NO];
|
||||
[_closeButton setHidden:YES];
|
||||
[_closeLabel setHidden:YES];
|
||||
}
|
||||
|
||||
- (void)switchToWaitingConnectView
|
||||
{
|
||||
[_rejectButton setHidden:YES];
|
||||
[_acceptButton setHidden:YES];
|
||||
[_cancelRequestButton setHidden:YES];
|
||||
[_footerView setHidden:YES];
|
||||
[_hintTextLabel setHidden:NO];
|
||||
[_avatarImageView setHidden:NO];
|
||||
[_nameTextLabel setHidden:NO];
|
||||
[_closeButton setHidden:NO];
|
||||
[_closeLabel setHidden:NO];
|
||||
}
|
||||
|
||||
- (void)switchToConnectedView
|
||||
{
|
||||
[_rejectButton setHidden:YES];
|
||||
[_acceptButton setHidden:YES];
|
||||
[_cancelRequestButton setHidden:YES];
|
||||
[_footerView setHidden:NO];
|
||||
[_muteButton setHidden:!_audioConnected];
|
||||
[self showDrawView];
|
||||
[_hintTextLabel setHidden:YES];
|
||||
[_avatarImageView setHidden:YES];
|
||||
[_nameTextLabel setHidden:YES];
|
||||
[_closeButton setHidden:NO];
|
||||
[_closeLabel setHidden:NO];
|
||||
}
|
||||
|
||||
- (void)showDrawView
|
||||
{
|
||||
CGRect frame = self.view.bounds;
|
||||
_drawViewWidth = frame.size.width;
|
||||
|
||||
frame.origin.y = (frame.size.height - frame.size.width) / 2;
|
||||
frame.size.height = _drawViewWidth;
|
||||
|
||||
_myDrawView = [[NTESWhiteboardDrawView alloc] initWithFrame:frame];
|
||||
_myDrawView.layer.borderWidth = 0.5;
|
||||
_myDrawView.layer.borderColor = UIColorFromRGB(0x9FA1A5).CGColor;
|
||||
_myDrawView.backgroundColor = [UIColor whiteColor];
|
||||
[_myDrawView setLineColor:[UIColor redColor]];
|
||||
[self.view addSubview:_myDrawView];
|
||||
|
||||
_peerDrawView = [[NTESWhiteboardDrawView alloc] initWithFrame:frame];
|
||||
_peerDrawView.layer.borderWidth = 0.5;
|
||||
_peerDrawView.layer.borderColor = UIColorFromRGB(0x9FA1A5).CGColor;
|
||||
_peerDrawView.backgroundColor = [UIColor clearColor];
|
||||
[_peerDrawView setLineColor:[UIColor greenColor]];
|
||||
[self.view addSubview:_peerDrawView];
|
||||
}
|
||||
|
||||
- (void)updateButton
|
||||
{
|
||||
NSString *image = _mute? @"btn_whiteboard_sound_close" : @"btn_whiteboard_sound_open";
|
||||
[_muteButton setImage:[UIImage imageNamed:image] forState:UIControlStateNormal];
|
||||
[_muteButton setImage:[UIImage imageNamed:image] forState:UIControlStateHighlighted];
|
||||
}
|
||||
|
||||
- (void)dismissAfter:(NSTimeInterval)seconds
|
||||
{
|
||||
[self.view setUserInteractionEnabled:NO];
|
||||
__weak typeof(self) wself = self;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[wself dismiss];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dismiss{
|
||||
if (_dismissed) {
|
||||
return;
|
||||
}
|
||||
_dismissed = YES;
|
||||
id<NIMRTSManager> manager = [NIMAVChatSDK sharedSDK].rtsManager;
|
||||
[manager removeDelegate:self];
|
||||
|
||||
NTESWhiteboardAttachment *attachment = [[NTESWhiteboardAttachment alloc] init];
|
||||
attachment.flag = CustomWhiteboardFlagClose;
|
||||
NIMMessage *message = [NTESSessionMsgConverter msgWithWhiteboardAttachment:attachment];
|
||||
[[[NIMSDK sharedSDK] conversationManager] saveMessage:message
|
||||
forSession:[NIMSession session:_peerID type:NIMSessionTypeP2P]
|
||||
completion:nil];
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
|
||||
[self dismissViewControllerAnimated:NO completion:^{
|
||||
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:NO];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)makeToast:(NSString *)toast
|
||||
{
|
||||
[self.view.window makeToast:toast ];
|
||||
}
|
||||
|
||||
- (void)sendMessage:(NIMMessage *)message
|
||||
{
|
||||
[[[NIMSDK sharedSDK] chatManager] sendMessage:message
|
||||
toSession:[NIMSession session:_peerID type:NIMSessionTypeP2P]
|
||||
error:nil];
|
||||
}
|
||||
|
||||
//发送纯命令, 不带参数
|
||||
- (void)sendWhiteboardCmd:(WhiteBoardCmdType)cmd
|
||||
{
|
||||
NSString *cmdString = [NSString stringWithFormat:@"%zd:0,0;", cmd];
|
||||
[self addCmd:cmdString];
|
||||
}
|
||||
|
||||
- (void)clearWhiteboard
|
||||
{
|
||||
[_myDrawView clear];
|
||||
[_peerDrawView clear];
|
||||
}
|
||||
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
|
||||
return UIInterfaceOrientationMaskPortrait;
|
||||
}
|
||||
|
||||
- (void)fillUserSetting:(NIMRTSOption *)option
|
||||
{
|
||||
option.serverRecordAudio = [[NTESBundleSetting sharedConfig] serverRecordAudio];
|
||||
option.serverRecordData = [[NTESBundleSetting sharedConfig] serverRecordWhiteboardData];
|
||||
option.autoDeactivateAudioSession = [[NTESBundleSetting sharedConfig] autoDeactivateAudioSession];
|
||||
option.audioDenoise = [[NTESBundleSetting sharedConfig] audioDenoise];
|
||||
option.voiceDetect = [[NTESBundleSetting sharedConfig] voiceDetect];
|
||||
option.preferHDAudio = [[NTESBundleSetting sharedConfig] voiceDetect];
|
||||
}
|
||||
@end
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
|
||||
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NTESWhiteboardViewController">
|
||||
<connections>
|
||||
<outlet property="acceptButton" destination="7vu-aS-TjX" id="R3w-Oy-X5i"/>
|
||||
<outlet property="avatarImageView" destination="yZH-Ut-wDn" id="Esj-AF-Z0A"/>
|
||||
<outlet property="cancelButton" destination="q6D-wx-SjQ" id="X4I-3K-VJM"/>
|
||||
<outlet property="cancelRequestButton" destination="8WR-3R-AQN" id="ZLa-ry-GE6"/>
|
||||
<outlet property="clearButton" destination="ni9-uq-Bos" id="uQk-Ik-yRu"/>
|
||||
<outlet property="closeButton" destination="tqS-HJ-chu" id="SuD-Mr-RKw"/>
|
||||
<outlet property="closeLabel" destination="Yku-Bo-Ahs" id="RKB-jE-rDG"/>
|
||||
<outlet property="footerView" destination="GUl-6b-I66" id="AOO-HO-ppm"/>
|
||||
<outlet property="headerView" destination="IA5-en-Rvi" id="hg0-cn-xdf"/>
|
||||
<outlet property="hintTextLabel" destination="1xG-NA-CJG" id="EI2-SH-5r2"/>
|
||||
<outlet property="muteButton" destination="uoe-LB-bRb" id="mNZ-wR-ah7"/>
|
||||
<outlet property="nameTextLabel" destination="duf-3o-HXh" id="OpM-v4-Gc5"/>
|
||||
<outlet property="rejectButton" destination="fKU-v5-Wvf" id="dya-yO-R4f"/>
|
||||
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8WR-3R-AQN" userLabel="取消呼叫">
|
||||
<rect key="frame" x="5" y="514" width="310" height="46"/>
|
||||
<color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="46" id="8vf-AC-49f"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||
<state key="normal" title="取消">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onCancelRequestButtonPressed:" destination="-1" eventType="touchUpInside" id="pv6-i6-foO"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fKU-v5-Wvf">
|
||||
<rect key="frame" x="5" y="514" width="152" height="46"/>
|
||||
<color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="150" id="1TI-y8-9MP"/>
|
||||
<constraint firstAttribute="height" constant="46" id="Ud5-2v-LDD"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||
<state key="normal" title="拒绝">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="1TI-y8-9MP"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<connections>
|
||||
<action selector="onRejectButtonPressed:" destination="-1" eventType="touchUpInside" id="is6-bB-sU1"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7vu-aS-TjX">
|
||||
<rect key="frame" x="163" y="514" width="152" height="46"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="150" id="G67-l1-uMF"/>
|
||||
<constraint firstAttribute="height" constant="46" id="v6C-5o-hx2"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||
<state key="normal" title="接受">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="G67-l1-uMF"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<connections>
|
||||
<action selector="onAcceptButtonPressed:" destination="-1" eventType="touchUpInside" id="4Z7-M8-6ud"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="HintText" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1xG-NA-CJG" userLabel="HintTextLabel">
|
||||
<rect key="frame" x="0.0" y="341" width="320" height="17"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NameText" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="duf-3o-HXh" userLabel="NameTextLabel">
|
||||
<rect key="frame" x="0.0" y="249" width="320" height="17"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IA5-en-Rvi" userLabel="HeaderView">
|
||||
<rect key="frame" x="-1" y="-1" width="321" height="65"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="白板" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Djc-nn-Ye7">
|
||||
<rect key="frame" x="136" y="35" width="50" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="50" id="KsU-So-c87"/>
|
||||
<constraint firstAttribute="height" constant="17" id="owz-vj-1c7"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="fill" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tqS-HJ-chu" userLabel="关闭">
|
||||
<rect key="frame" x="258" y="0.0" width="64" height="64"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="64" id="IOF-wp-Dss"/>
|
||||
<constraint firstAttribute="width" constant="64" id="OmS-vk-d5L"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<inset key="contentEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal">
|
||||
<color key="titleColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onCloseButtonPressed:" destination="-1" eventType="touchUpInside" id="b91-xv-vo1"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="关闭" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Yku-Bo-Ahs">
|
||||
<rect key="frame" x="263" y="35" width="50" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="17" id="GWd-86-NJ5"/>
|
||||
<constraint firstAttribute="width" constant="50" id="bz3-FL-NnI"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="65" id="7F6-1y-w4a"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tqS-HJ-chu" secondAttribute="trailing" constant="-1" id="8Gd-5o-yMp"/>
|
||||
<constraint firstItem="Yku-Bo-Ahs" firstAttribute="top" secondItem="IA5-en-Rvi" secondAttribute="top" constant="35" id="9Sb-f5-OEX"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Yku-Bo-Ahs" secondAttribute="trailing" constant="8" id="Gfw-gZ-KQL"/>
|
||||
<constraint firstItem="tqS-HJ-chu" firstAttribute="top" secondItem="IA5-en-Rvi" secondAttribute="top" id="Szv-Ev-ug6"/>
|
||||
<constraint firstItem="Djc-nn-Ye7" firstAttribute="top" secondItem="IA5-en-Rvi" secondAttribute="top" constant="35" id="cco-fp-RG1"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Djc-nn-Ye7" secondAttribute="centerX" id="j0f-XZ-3qL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yZH-Ut-wDn" userLabel="AvatarImageView" customClass="NIMAvatarImageView">
|
||||
<rect key="frame" x="120" y="154" width="80" height="80"/>
|
||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="80" id="XZ1-Nn-d8o"/>
|
||||
<constraint firstAttribute="height" constant="80" id="nka-HF-n6E"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GUl-6b-I66" userLabel="FooterView">
|
||||
<rect key="frame" x="-1" y="498" width="322" height="71"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uoe-LB-bRb" userLabel="声音">
|
||||
<rect key="frame" x="0.0" y="0.0" width="65" height="71"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="65" id="hPt-Pl-dze"/>
|
||||
</constraints>
|
||||
<state key="normal" image="btn_whiteboard_sound_open">
|
||||
<color key="titleColor" red="1" green="0.89332819845508771" blue="0.42760693648734971" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onMuteButtonPressed:" destination="-1" eventType="touchUpInside" id="Hoo-bH-NSQ"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ni9-uq-Bos">
|
||||
<rect key="frame" x="247" y="20" width="60" height="31"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="60" id="AaH-kr-7li"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="清空">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onClearLinesButtonPressed:" destination="-1" eventType="touchUpInside" id="UQf-Rf-rIT"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q6D-wx-SjQ">
|
||||
<rect key="frame" x="172" y="20" width="60" height="31"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="60" id="MCA-eI-rgJ"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="上一步">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onCancelButtonPressed:" destination="-1" eventType="touchUpInside" id="2M3-Hj-Hbf"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="uoe-LB-bRb" secondAttribute="bottom" id="7Jm-zL-vGJ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="ni9-uq-Bos" secondAttribute="bottom" constant="20" id="DGB-fu-r0F"/>
|
||||
<constraint firstAttribute="bottom" secondItem="q6D-wx-SjQ" secondAttribute="bottom" constant="20" id="Dwt-sB-ZY9"/>
|
||||
<constraint firstItem="uoe-LB-bRb" firstAttribute="top" secondItem="GUl-6b-I66" secondAttribute="top" id="IH2-7n-F6K"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ni9-uq-Bos" secondAttribute="trailing" constant="15" id="SLa-Su-IBf"/>
|
||||
<constraint firstItem="q6D-wx-SjQ" firstAttribute="leading" secondItem="ni9-uq-Bos" secondAttribute="trailing" constant="20" id="SkQ-d3-cNz"/>
|
||||
<constraint firstItem="uoe-LB-bRb" firstAttribute="leading" secondItem="GUl-6b-I66" secondAttribute="leading" id="X40-Ec-WBk"/>
|
||||
<constraint firstAttribute="trailing" secondItem="q6D-wx-SjQ" secondAttribute="trailing" constant="15" id="YcD-0c-hXF"/>
|
||||
<constraint firstAttribute="height" constant="71" id="dkZ-qz-tIo"/>
|
||||
<constraint firstItem="ni9-uq-Bos" firstAttribute="top" secondItem="GUl-6b-I66" secondAttribute="top" constant="20" id="lbP-VR-MeO"/>
|
||||
<constraint firstItem="q6D-wx-SjQ" firstAttribute="top" secondItem="GUl-6b-I66" secondAttribute="top" constant="20" id="lsP-v8-eH2"/>
|
||||
<constraint firstItem="ni9-uq-Bos" firstAttribute="leading" secondItem="q6D-wx-SjQ" secondAttribute="trailing" constant="15" id="sxo-P1-X0p"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="SkQ-d3-cNz"/>
|
||||
<exclude reference="YcD-0c-hXF"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.80000001192092896" green="0.80000001192092896" blue="0.80000001192092896" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="7vu-aS-TjX" secondAttribute="trailing" constant="5" id="4Fx-lT-S43"/>
|
||||
<constraint firstItem="yZH-Ut-wDn" firstAttribute="top" secondItem="IA5-en-Rvi" secondAttribute="bottom" constant="90" id="6Je-P7-pE7"/>
|
||||
<constraint firstAttribute="trailing" secondItem="IA5-en-Rvi" secondAttribute="trailing" id="BZa-Za-DeS"/>
|
||||
<constraint firstItem="1xG-NA-CJG" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="Bbq-Aj-MD7"/>
|
||||
<constraint firstItem="IA5-en-Rvi" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="-1" id="FPT-ns-wKV"/>
|
||||
<constraint firstItem="1xG-NA-CJG" firstAttribute="centerX" secondItem="yZH-Ut-wDn" secondAttribute="centerX" id="HiP-Vr-0dP"/>
|
||||
<constraint firstItem="GUl-6b-I66" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="-1" id="IDg-DC-6vQ"/>
|
||||
<constraint firstItem="duf-3o-HXh" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="IVf-kH-uGV"/>
|
||||
<constraint firstAttribute="trailing" secondItem="GUl-6b-I66" secondAttribute="trailing" constant="-1" id="KHE-KO-kKy"/>
|
||||
<constraint firstAttribute="trailing" secondItem="duf-3o-HXh" secondAttribute="trailing" id="Mgp-fK-tbH"/>
|
||||
<constraint firstAttribute="bottom" secondItem="7vu-aS-TjX" secondAttribute="bottom" constant="8" id="PAw-oT-rsl"/>
|
||||
<constraint firstItem="duf-3o-HXh" firstAttribute="top" secondItem="yZH-Ut-wDn" secondAttribute="bottom" constant="15" id="Swt-7Z-8Dj"/>
|
||||
<constraint firstItem="IA5-en-Rvi" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="-1" id="UAl-kb-h5e"/>
|
||||
<constraint firstItem="yZH-Ut-wDn" firstAttribute="centerX" secondItem="fKU-v5-Wvf" secondAttribute="trailing" constant="3" id="bpO-Pu-8iZ"/>
|
||||
<constraint firstItem="8WR-3R-AQN" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="5" id="gG3-QQ-yjf"/>
|
||||
<constraint firstAttribute="bottom" secondItem="fKU-v5-Wvf" secondAttribute="bottom" constant="8" id="hWQ-JO-WMn"/>
|
||||
<constraint firstItem="fKU-v5-Wvf" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="5" id="izZ-Ns-1Qa"/>
|
||||
<constraint firstItem="yZH-Ut-wDn" firstAttribute="centerX" secondItem="7vu-aS-TjX" secondAttribute="leading" constant="-3" id="mpJ-Ag-PSJ"/>
|
||||
<constraint firstItem="fKU-v5-Wvf" firstAttribute="bottom" secondItem="8WR-3R-AQN" secondAttribute="bottom" id="o4Q-ew-KRJ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="8WR-3R-AQN" secondAttribute="trailing" constant="5" id="sjW-Pb-n1t"/>
|
||||
<constraint firstAttribute="trailing" secondItem="1xG-NA-CJG" secondAttribute="trailing" id="sy0-Ua-l1a"/>
|
||||
<constraint firstItem="1xG-NA-CJG" firstAttribute="top" secondItem="duf-3o-HXh" secondAttribute="bottom" constant="75" id="uSm-AM-3Be"/>
|
||||
<constraint firstAttribute="bottom" secondItem="GUl-6b-I66" secondAttribute="bottom" constant="-1" id="ufz-kB-62Q"/>
|
||||
</constraints>
|
||||
<simulatedScreenMetrics key="simulatedDestinationMetrics" type="retina4"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="btn_whiteboard_sound_open" width="35" height="35"/>
|
||||
</resources>
|
||||
</document>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// WhiteBoardDrawView.h
|
||||
// NIM
|
||||
//
|
||||
// Created by 高峰 on 15/7/1.
|
||||
// Copyright (c) 2015年 Netease. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface NTESWhiteboardDrawView : UIView
|
||||
|
||||
- (void)setLineColor:(UIColor *)color;
|
||||
|
||||
- (void)addPoints:(NSMutableArray *)points isNewLine:(BOOL)newLine;
|
||||
|
||||
- (void)deleteLastLine;
|
||||
|
||||
- (void)clear;
|
||||
|
||||
@end
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// WhiteBoardDrawView.m
|
||||
// NIM
|
||||
//
|
||||
// Created by 高峰 on 15/7/1.
|
||||
// Copyright (c) 2015年 Netease. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NTESWhiteboardDrawView.h"
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "NTESCADisplayLinkHolder.h"
|
||||
|
||||
@interface NTESWhiteboardDrawView()<NTESCADisplayLinkHolderDelegate>
|
||||
|
||||
@property(nonatomic, strong)NSMutableArray *myLines;
|
||||
@property(nonatomic, assign)BOOL shouldDraw;
|
||||
|
||||
@property(nonatomic, strong)NTESCADisplayLinkHolder *displayLinkHolder;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NTESWhiteboardDrawView
|
||||
|
||||
#pragma mark - public methods
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
_myLines = [[NSMutableArray alloc] init];
|
||||
|
||||
CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
|
||||
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
|
||||
shapeLayer.fillColor = [UIColor clearColor].CGColor;
|
||||
shapeLayer.lineJoin = kCALineJoinRound;
|
||||
shapeLayer.lineCap = kCALineCapRound;
|
||||
shapeLayer.lineWidth = 2;
|
||||
shapeLayer.masksToBounds = YES;
|
||||
|
||||
_displayLinkHolder = [[NTESCADisplayLinkHolder alloc] init];
|
||||
[_displayLinkHolder setFrameInterval:3];
|
||||
[_displayLinkHolder startCADisplayLinkWithDelegate:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
[_displayLinkHolder stop];
|
||||
}
|
||||
|
||||
+ (Class)layerClass
|
||||
{
|
||||
return [CAShapeLayer class];
|
||||
}
|
||||
|
||||
- (void)setLineColor:(UIColor *)color
|
||||
{
|
||||
CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
|
||||
shapeLayer.strokeColor = color.CGColor;
|
||||
}
|
||||
|
||||
- (void)addPoints:(NSMutableArray *)points isNewLine:(BOOL)newLine
|
||||
{
|
||||
__weak typeof(self) wself = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[wself addPoints:points toLines:[wself myLines] isNewLine:newLine];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)deleteLastLine
|
||||
{
|
||||
__weak typeof(self) wself = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[wself deleteLastLine:[wself myLines]];
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
- (void)deleteAllLines
|
||||
{
|
||||
__weak typeof(self) wself = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[wself deleteAllLines:[wself myLines]];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)clear
|
||||
{
|
||||
[self deleteAllLines];
|
||||
}
|
||||
|
||||
#pragma mark - private methods
|
||||
- (void)addPoints:(NSMutableArray *)points
|
||||
toLines:(NSMutableArray *)lines
|
||||
isNewLine:(BOOL)newLine
|
||||
{
|
||||
if (newLine) {
|
||||
[lines addObject:points];
|
||||
}
|
||||
else if (lines.count == 0) {
|
||||
[lines addObject:points];
|
||||
}
|
||||
else {
|
||||
NSMutableArray *lastLine = [lines lastObject];
|
||||
[lastLine addObjectsFromArray:points];
|
||||
}
|
||||
_shouldDraw = YES;
|
||||
}
|
||||
|
||||
-(void)deleteLastLine:(NSMutableArray *)lines
|
||||
{
|
||||
[lines removeLastObject];
|
||||
_shouldDraw = YES;
|
||||
}
|
||||
|
||||
-(void)deleteAllLines:(NSMutableArray *)lines
|
||||
{
|
||||
[lines removeAllObjects];
|
||||
_shouldDraw = YES;
|
||||
}
|
||||
|
||||
- (void)onDisplayLinkFire:(NTESCADisplayLinkHolder *)holder
|
||||
duration:(NSTimeInterval)duration
|
||||
displayLink:(CADisplayLink *)displayLink
|
||||
{
|
||||
if (!_shouldDraw) {
|
||||
return;
|
||||
}
|
||||
UIBezierPath *path = [[UIBezierPath alloc] init];
|
||||
NSUInteger linesCount = _myLines.count;
|
||||
for (NSUInteger i = 0 ; i < linesCount; i ++) {
|
||||
NSArray *line = [_myLines objectAtIndex:i];
|
||||
NSUInteger pointsCount = line.count;
|
||||
for (NSUInteger j = 0 ; j < pointsCount; j ++) {
|
||||
NSArray *point = [line objectAtIndex:j];
|
||||
CGPoint p = CGPointMake([point[0] floatValue], [point[1] floatValue]);
|
||||
if (j == 0) {
|
||||
[path moveToPoint:p];
|
||||
}
|
||||
else {
|
||||
[path addLineToPoint:p];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
((CAShapeLayer *)self.layer).path = path.CGPath;
|
||||
_shouldDraw = NO;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user