Initial commit

This commit is contained in:
ifish
2017-08-15 16:59:01 +08:00
commit bdc83e07ef
5766 changed files with 423223 additions and 0 deletions
@@ -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
@@ -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
@@ -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>
@@ -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
@@ -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