KSWhiteboardView.m 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. //
  2. // KSWhiteboardView.m
  3. // TeacherDaya
  4. //
  5. // Created by Kyle on 2019/10/31.
  6. // Copyright © 2019 DayaMusic. All rights reserved.
  7. //
  8. #import "KSWhiteboardView.h"
  9. #import <Masonry/Masonry.h>
  10. #import "ZoomControl.h"
  11. #import <Masonry/Masonry.h>
  12. #import "WhiteUtils.h"
  13. #import <UIImage+Color.h>
  14. #define kSceneKey @"/DAYA"
  15. #define MAXPHOTONUMBER (1)
  16. #define COLUMNNUMBER (3)
  17. #import "TZImageManager.h"
  18. #import <AssetsLibrary/AssetsLibrary.h>
  19. #import <Photos/Photos.h>
  20. #import "TZVideoPlayerController.h"
  21. #import "TZImagePickerController.h"
  22. #import <UIImage+ResizeImage.h>
  23. #import "KSNormalAlertView.h"
  24. @interface KSWhiteboardView ()<ZoomControlDelegate,WhiteCommonCallbackDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,TZImagePickerControllerDelegate,WhiteRoomCallbackDelegate>
  25. {
  26. BOOL _isSelectOriginalPhoto;
  27. }
  28. @property (nonatomic, strong) UIImagePickerController *imagePickerVc;
  29. @property (nonatomic, strong) NSMutableArray *imageArray; // 图片数组
  30. @property (nonatomic, strong) NSMutableArray *imageAsset; // 图片 asset
  31. //@property (nonatomic, strong) ZoomControl *zoomControl;
  32. @property (nonatomic, assign) BOOL isFullScreenMode;
  33. @property (nonatomic, weak) id<KSWhiteboardViewDelegate> delegate;
  34. @property (nonatomic, strong) UIView *backView;
  35. @property (nonatomic, strong, nonnull) WhiteSdkConfiguration *sdkConfig;
  36. @property (nonatomic, weak, nullable) id<WhiteRoomCallbackDelegate> roomCallbackDelegate;
  37. @property (nonatomic, copy) NSString *roomToken;
  38. @property (nonatomic, assign, getter=isReconnecting) BOOL reconnecting;
  39. @property (nonatomic, strong) WhiteCameraConfig *defaultConfig;
  40. @property (nonatomic, strong) NSString *currentSceneDir;
  41. @property (nonatomic, strong) UIView *toobarView;
  42. @property (nonatomic, strong) NSMutableArray *sceneArray;
  43. @property (nonatomic, assign) BOOL isConnected; // 是否加入房间
  44. @end
  45. @implementation KSWhiteboardView
  46. - (instancetype)initWithDelegate:(id<KSWhiteboardViewDelegate>)delegate frame:(CGRect)frame {
  47. self = [super initWithFrame:frame];
  48. if (self) {
  49. self.delegate = delegate;
  50. [self addSubview:self.boardView];
  51. [self addSubview:self.backView];
  52. [self.boardView mas_makeConstraints:^(MASConstraintMaker *make) {
  53. make.edges.mas_equalTo(self);
  54. }];
  55. [self.backView mas_makeConstraints:^(MASConstraintMaker *make) {
  56. make.edges.mas_equalTo(self);
  57. }];
  58. [self initSDK];
  59. }
  60. return self;
  61. }
  62. - (void)createToolBar {
  63. UIButton *preButton = [UIButton buttonWithType:UIButtonTypeCustom];
  64. [preButton setImage:[UIImage imageNamed:@"whiteBoard_left"] forState:UIControlStateNormal];
  65. [preButton setImage:[UIImage imageNamed:@"whiteBoard_left_h"] forState:UIControlStateHighlighted];
  66. [preButton addTarget:self action:@selector(preWhiteBoardPage) forControlEvents:UIControlEventTouchUpInside];
  67. [self addSubview:preButton];
  68. [preButton mas_makeConstraints:^(MASConstraintMaker *make) {
  69. make.left.mas_equalTo(self);
  70. make.centerY.mas_equalTo(self.mas_centerY);
  71. make.width.mas_equalTo(36);
  72. make.height.mas_equalTo(36);
  73. }];
  74. UIButton *nextButton = [UIButton buttonWithType:UIButtonTypeCustom];
  75. [nextButton setImage:[UIImage imageNamed:@"whiteBoard_right"] forState:UIControlStateNormal];
  76. [nextButton setImage:[UIImage imageNamed:@"whiteBoard_right_h"] forState:UIControlStateHighlighted];
  77. [nextButton addTarget:self action:@selector(nextWhiteBoardPage) forControlEvents:UIControlEventTouchUpInside];
  78. [self addSubview:nextButton];
  79. [nextButton mas_makeConstraints:^(MASConstraintMaker *make) {
  80. make.right.mas_equalTo(self);
  81. make.centerY.mas_equalTo(self.mas_centerY);
  82. make.width.mas_equalTo(36);
  83. make.height.mas_equalTo(36);
  84. }];
  85. _toobarView = [[UIView alloc] init];
  86. _toobarView.backgroundColor = [UIColor whiteColor];
  87. _toobarView.layer.borderWidth = 1.0f;
  88. _toobarView.layer.borderColor = HexRGB(0xe5e5e5).CGColor;
  89. _toobarView.layer.cornerRadius = 4.0f;
  90. [self addSubview:_toobarView];
  91. [_toobarView mas_makeConstraints:^(MASConstraintMaker *make) {
  92. make.centerX.mas_equalTo(self.mas_centerX);
  93. make.bottom.mas_equalTo(self.mas_bottom).offset(-15);
  94. make.height.mas_equalTo(40);
  95. make.width.mas_equalTo(36*7);
  96. }];
  97. [self createToolButton];
  98. }
  99. - (void)createToolButton {
  100. NSArray *imageArray = @[@"whiteboard_pencil",@"whiteboard_eraser",@"whiteboard_ellipse",@"whiteboard_clear",@"whiteboard_delete",@"whiteboard_add",@"whiteboard_upload"];
  101. NSArray *titleArray = @[@"画笔",@"橡皮",@"椭圆",@"清空",@"删除",@"新增",@"上传"];
  102. for (NSInteger i = 0; i < titleArray.count; i++) {
  103. UIButton *toolButton = [UIButton buttonWithType:UIButtonTypeCustom];
  104. toolButton.frame = CGRectMake(i*36, 2, 36, 36);
  105. [toolButton setImage:[UIImage imageNamed:imageArray[i]] forState:UIControlStateNormal];
  106. [toolButton setImage:[[UIImage imageNamed:imageArray[i]] maskWithColor:THEMECOLOR] forState:UIControlStateSelected];
  107. [toolButton setImage:[[UIImage imageNamed:imageArray[i]] maskWithColor:THEMECOLOR] forState:UIControlStateHighlighted];
  108. toolButton.tag = 1000+i;
  109. [toolButton.titleLabel setFont:[UIFont systemFontOfSize:10.0f]];
  110. [toolButton addTarget:self action:@selector(toolButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
  111. [_toobarView addSubview:toolButton];
  112. }
  113. }
  114. - (void)toolButtonClickAction:(UIButton *)sender {
  115. NSInteger index = sender.tag - 1000;
  116. if (index == 0 || index == 1 || index == 2) {
  117. UIButton *button = [self.toobarView viewWithTag:1000];
  118. [button setSelected:NO];
  119. UIButton *button1 = [self.toobarView viewWithTag:1001];
  120. [button1 setSelected:NO];
  121. UIButton *button2 = [self.toobarView viewWithTag:1002];
  122. [button2 setSelected:NO];
  123. sender.selected = YES;
  124. }
  125. switch (index) {
  126. case 0: // 铅笔颜色
  127. {
  128. WhiteMemberState *memberState = [[WhiteMemberState alloc] init];
  129. //白板初始状态时,教具默认为 pencil
  130. memberState.currentApplianceName = AppliancePencil;
  131. // memberState.strokeColor = @[@(255),@(0),@(0)];
  132. // memberState.strokeWidth = @2;
  133. [self.room setMemberState:memberState];
  134. }
  135. break;
  136. case 1: // 橡皮
  137. {
  138. WhiteMemberState *memberState = [[WhiteMemberState alloc] init];
  139. memberState.currentApplianceName = ApplianceEraser;
  140. [self.room setMemberState:memberState];
  141. }
  142. break;
  143. case 2: // 椭圆
  144. {
  145. WhiteMemberState *memberState = [[WhiteMemberState alloc] init];
  146. //白板初始状态时,教具默认为 pencil
  147. memberState.currentApplianceName = ApplianceEllipse;
  148. [self.room setMemberState:memberState];
  149. }
  150. break;
  151. case 3: // 清空
  152. {
  153. [self.room cleanScene:YES];
  154. }
  155. break;
  156. case 4: // 删除
  157. {
  158. [self deleateWhiteBoard];
  159. }
  160. break;
  161. case 5: // 新增
  162. {
  163. [self createNewWhiteBoard];
  164. }
  165. break;
  166. case 6: // 上传
  167. {
  168. [self pushImagePickerController];
  169. }
  170. break;
  171. default:
  172. break;
  173. }
  174. }
  175. #pragma mark --- 删除白板
  176. - (void)deleateWhiteBoard {
  177. if ([self.sceneArray count] <= 1) {
  178. [self.room cleanScene:NO];
  179. return;
  180. }
  181. else {
  182. NSInteger delIndex = 0;
  183. for (NSInteger i = 0; i < [self.sceneArray count]; i++) {
  184. WhiteScene *sc = (WhiteScene *)self.sceneArray[i];
  185. NSString *s = [self whiteBoardPageName:sc.name];
  186. if ([s isEqualToString:self.currentSceneDir]) {
  187. delIndex = i;
  188. break;
  189. }
  190. }
  191. [self.sceneArray removeObjectAtIndex:delIndex];
  192. WhiteScene *sc;
  193. if (delIndex < [self.sceneArray count]) {
  194. sc = (WhiteScene *)self.sceneArray[delIndex];
  195. }
  196. else {
  197. sc = (WhiteScene *)[self.sceneArray lastObject];
  198. }
  199. [self.room removeScenes:self.currentSceneDir];
  200. self.currentSceneDir = [self whiteBoardPageName:sc.name];
  201. }
  202. }
  203. #pragma mark ---- 新增白板
  204. - (void)createNewWhiteBoard {
  205. NSString *pageName = [NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]];
  206. WhitePptPage *pptPage = [[WhitePptPage alloc] init];
  207. WhiteScene *scene = [[WhiteScene alloc] initWithName:pageName ppt:pptPage];
  208. [self.sceneArray addObject:scene];
  209. NSInteger pageCount = [self.sceneArray count];
  210. //插入新页面的 API,现在支持传入 ppt 参数(可选),所以插入PPT和插入新页面的 API,合并成了一个。
  211. [self.room putScenes:kSceneKey scenes:@[scene] index:pageCount];
  212. NSString *newScenePath = [self whiteBoardPageName:pageName];
  213. [self.room setScenePath:newScenePath];
  214. self.currentSceneDir = newScenePath;
  215. }
  216. #pragma mark ---- 上传图片
  217. - (void)uploadPPt {
  218. UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:IS_IPAD ? UIAlertControllerStyleAlert : UIAlertControllerStyleActionSheet];
  219. [alertVC addAction:[UIAlertAction actionWithTitle:@"相机拍摄" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  220. // 调用相机
  221. [self takePhoto];
  222. }]];
  223. [alertVC addAction:[UIAlertAction actionWithTitle:@"从手机相册选择" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  224. // 调用相册
  225. [self pushImagePickerController];
  226. }]];
  227. [alertVC addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  228. }]];
  229. alertVC.modalPresentationStyle = UIModalPresentationFullScreen;
  230. [[self getViewController] presentViewController:alertVC animated:true completion:nil];
  231. }
  232. #pragma mark ------ 上一页
  233. - (void)preWhiteBoardPage
  234. {
  235. if (![self.sceneArray count]) {
  236. DLog(@"last page return");
  237. return;
  238. }
  239. else if ([self.sceneArray count] == 1) {
  240. WhiteScene *sc = (WhiteScene *)[self.sceneArray firstObject];
  241. self.currentSceneDir = [self whiteBoardPageName:sc.name];
  242. }
  243. else {
  244. NSString *tmpScName = self.currentSceneDir;
  245. for (WhiteScene *sc in self.sceneArray) {
  246. NSString *s = [self whiteBoardPageName:sc.name];
  247. if ([s isEqualToString:self.currentSceneDir]) {
  248. self.currentSceneDir = tmpScName;
  249. break;
  250. }
  251. else {
  252. tmpScName = s;
  253. }
  254. }
  255. }
  256. DLog(@"last page: %@", self.currentSceneDir);
  257. [self.room setScenePath:self.currentSceneDir];
  258. }
  259. #pragma mark ------ 下一页
  260. - (void)nextWhiteBoardPage
  261. {
  262. if (![self.sceneArray count]) {
  263. DLog(@"next page return");
  264. return;
  265. }
  266. else if ([self.sceneArray count] == 1) {
  267. WhiteScene *sc = (WhiteScene *)[self.sceneArray firstObject];
  268. self.currentSceneDir = [self whiteBoardPageName:sc.name];
  269. }
  270. else {
  271. NSInteger matchIndex = 0;
  272. for (NSInteger i = 0; i < [self.sceneArray count]; i++) {
  273. WhiteScene *sc = (WhiteScene *)self.sceneArray[i];
  274. NSString *s = [self whiteBoardPageName:sc.name];
  275. if ([s isEqualToString:self.currentSceneDir]) {
  276. matchIndex = i + 1;
  277. break;
  278. }
  279. }
  280. WhiteScene *tsc;
  281. if (matchIndex < [self.sceneArray count]) {
  282. tsc = (WhiteScene *)self.sceneArray[matchIndex];
  283. }
  284. else {
  285. tsc = (WhiteScene *)[self.sceneArray lastObject];
  286. }
  287. self.currentSceneDir = [self whiteBoardPageName:tsc.name];
  288. }
  289. DLog(@"next page: %@", self.currentSceneDir);
  290. [self.room setScenePath:self.currentSceneDir];
  291. }
  292. - (void)reloadWhiteBoardWithName:(NSString *)name {
  293. self.currentSceneDir = [self whiteBoardPageName:name];
  294. [self.room setScenePath:self.currentSceneDir];
  295. }
  296. - (void)destroy {
  297. [self.boardView stopLoading];
  298. [self removeFromSuperview];
  299. }
  300. - (void)setCurrentFrame:(CGRect)currentFrame {
  301. _currentFrame = currentFrame;
  302. if (!self.isFullScreenMode) {
  303. self.frame = currentFrame;
  304. }
  305. [self.room refreshViewSize];
  306. }
  307. #pragma mark - ZoomControlDelegate
  308. - (void)zoomControlDelegate:(CGFloat)scale{
  309. self.clipsToBounds = YES;
  310. CGSize baseSize = self.frame.size;
  311. baseSize.height *= scale;
  312. baseSize.width *= scale;
  313. CGPoint center = self.boardView.center;
  314. CGRect frame = self.frame;
  315. frame.size = baseSize;
  316. self.boardView.frame = frame;
  317. self.boardView.center = center;
  318. if (self.delegate && [self.delegate respondsToSelector:@selector(whiteboardViewDidChangeZoomScale:)]) {
  319. [self.delegate whiteboardViewDidChangeZoomScale:scale];
  320. }
  321. }
  322. - (void)fullScreenDidUpdate:(BOOL)isFull{
  323. self.isFullScreenMode = isFull;
  324. UIView *superView = self.superview;
  325. if (self.isFullScreenMode) {
  326. [superView bringSubviewToFront:self];
  327. [UIView animateWithDuration:0.3 animations:^{
  328. self.frame = CGRectMake(0, 0, UIScreenWidth, UIScreenHeight);
  329. }];
  330. }else{
  331. [UIView animateWithDuration:0.3 animations:^{
  332. self.frame = self.currentFrame;
  333. }];
  334. }
  335. }
  336. - (WhiteBoardView *)boardView {
  337. if (!_boardView) {
  338. _boardView = [[WhiteBoardView alloc] init];
  339. }
  340. return _boardView;
  341. }
  342. - (UIView *)backView {
  343. if (!_backView) {
  344. _backView = [[UIView alloc] init];
  345. _backView.backgroundColor = [UIColor whiteColor];
  346. _backView.hidden = YES;
  347. }
  348. return _backView;
  349. }
  350. //- (ZoomControl *)zoomControl{
  351. // if (!_zoomControl) {
  352. // _zoomControl = [[ZoomControl alloc] init];
  353. // _zoomControl.delegate = self;
  354. // _zoomControl.increaseScaleBtn.hidden = YES;
  355. // _zoomControl.reduceScaleBtn.hidden = YES;
  356. // }
  357. // return _zoomControl;
  358. //}
  359. #pragma mark - CallbackDelegate
  360. - (id<WhiteRoomCallbackDelegate>)roomCallbackDelegate {
  361. if (!_roomCallbackDelegate) {
  362. _roomCallbackDelegate = self;
  363. }
  364. return _roomCallbackDelegate;
  365. }
  366. - (id<WhiteCommonCallbackDelegate>)commonDelegate
  367. {
  368. if (!_commonDelegate) {
  369. _commonDelegate = self;
  370. }
  371. return _commonDelegate;
  372. }
  373. #pragma mark - WhiteCommonCallbackDelegate
  374. - (void)sdkSetupFail:(NSError *)error {
  375. if (self.delegate && [self.delegate respondsToSelector:@selector(joinWhiteboardRoomStatus:)]) {
  376. [self.delegate joinWhiteboardRoomStatus:JOINROOMSTATUS_FAILED];
  377. }
  378. }
  379. - (void)throwError:(NSError *)error
  380. {
  381. NSLog(@"throwError: %@", error.userInfo);
  382. }
  383. - (NSString *)urlInterrupter:(NSString *)url
  384. {
  385. return @"https://white-pan.oss-cn-shanghai.aliyuncs.com/124/image/beauty2.png";
  386. }
  387. #pragma mark - WhiteSDK
  388. - (WhiteSdkConfiguration *)sdkConfig
  389. {
  390. if (!_sdkConfig) {
  391. // 4. 初始化 SDK 配置项,根据需求配置属性
  392. WhiteSdkConfiguration *config = [[WhiteSdkConfiguration alloc] initWithApp:@"614/YXPRtKkVCBOBjQ"];
  393. //如果不需要拦截图片API,则不需要开启,页面内容较为复杂时,可能会有性能问题
  394. config.enableInterrupterAPI = NO;
  395. //打开用户头像显示信息
  396. config.userCursor = NO;
  397. _sdkConfig = config;
  398. }
  399. return _sdkConfig;
  400. }
  401. - (void)initSDK {
  402. // 5.初始化 SDK,传入 commomDelegate
  403. self.sdk = [[WhiteSDK alloc] initWithWhiteBoardView:self.boardView config:self.sdkConfig commonCallbackDelegate:self.commonDelegate];
  404. }
  405. #pragma mark - Room Action
  406. - (void)joinWhiteRoom {
  407. if (self.isConnected) {
  408. return;
  409. }
  410. [WhiteUtils getRoomTokenWithRoomId:self.roomId completionHandler:^(NSString * _Nullable uuid, NSString * _Nullable roomToken, NSString * _Nonnull randomRoom, NSString * _Nullable randomNumeric, NSError * _Nullable error) {
  411. if (error) {
  412. NSLog(NSLocalizedString(@"创建房间失败,error:", nil), [error localizedDescription]);
  413. if (self.delegate && [self.delegate respondsToSelector:@selector(joinWhiteboardRoomStatus:)]) {
  414. [self.delegate joinWhiteboardRoomStatus:JOINROOMSTATUS_PARAMERROR];
  415. }
  416. }
  417. else {
  418. self.roomUuid = uuid;
  419. if (self.roomUuid && roomToken) {
  420. [self joinRoomWithToken:roomToken];
  421. } else {
  422. NSLog(NSLocalizedString(@"连接房间失败,room uuid:%@ roomToken:%@", nil), self.roomUuid, roomToken);
  423. if (self.delegate && [self.delegate respondsToSelector:@selector(joinWhiteboardRoomStatus:)]) {
  424. [self.delegate joinWhiteboardRoomStatus:JOINROOMSTATUS_PARAMERROR];
  425. }
  426. }
  427. }
  428. }];
  429. }
  430. - (void)joinRoomWithToken:(NSString *)roomToken
  431. {
  432. NSDictionary *payload = @{@"avatar": @"https://white-pan.oss-cn-shanghai.aliyuncs.com/40/image/mask.jpg"};
  433. WhiteRoomConfig *config = [[WhiteRoomConfig alloc] initWithUUID:self.roomUuid roomToken:self.roomToken uid:UserDefault(UIDKey)];
  434. config.isWritable = NO; // 设置学生只读
  435. config.disableEraseImage = YES;
  436. // 设置WhiteCameraBound
  437. [self.sdk joinRoomWithConfig:config callbacks:self.roomCallbackDelegate completionHandler:^(BOOL success, WhiteRoom * _Nonnull room, NSError * _Nonnull error) {
  438. if (success) {
  439. // 标记已加入房间
  440. self.isConnected = YES;
  441. self.roomToken = roomToken;
  442. self.room = room;
  443. // 设置主播模式
  444. [self.room setViewMode:WhiteViewModeBroadcaster];
  445. [self.room disableCameraTransform:NO];
  446. if (self.defaultConfig) {
  447. [self.room moveCamera:self.defaultConfig];
  448. }
  449. [self.room getSceneStateWithResult:^(WhiteSceneState * _Nonnull state) {
  450. if (state.scenes) {
  451. for (WhiteScene *tmpScene in state.scenes) {
  452. if (![tmpScene.name isEqualToString:@"init"]) {
  453. [self.sceneArray addObject:tmpScene];
  454. }
  455. }
  456. }
  457. DLog(@"state.scenePath: %@", state.scenePath);
  458. if ([state.scenePath isEqualToString:@"/init"]) {
  459. [self createNewWhiteBoard];
  460. }
  461. else {
  462. self.currentSceneDir = state.scenePath;
  463. }
  464. }];
  465. }
  466. else {
  467. if (self.delegate && [self.delegate respondsToSelector:@selector(joinWhiteboardRoomStatus:)]) {
  468. [self.delegate joinWhiteboardRoomStatus:JOINROOMSTATUS_FAILED];
  469. }
  470. NSLog(@"错误信息:%@",[error localizedDescription]);
  471. }
  472. }];
  473. }
  474. - (void)leaveRoom {
  475. if (self.room && self.isConnected) {
  476. [self.room disconnect:^{
  477. self.isConnected = NO;
  478. }];
  479. }
  480. }
  481. #pragma mark - Keyboard
  482. - (void)setRectangle {
  483. [self.room getSceneStateWithResult:^(WhiteSceneState * _Nonnull state) {
  484. if (state.scenes) {
  485. WhitePptPage *ppt = state.scenes[state.index].ppt;
  486. WhiteRectangleConfig *rectangle = [[WhiteRectangleConfig alloc] initWithInitialPosition:ppt.width height:ppt.height];
  487. [self.room moveCameraToContainer:rectangle];
  488. }
  489. }];
  490. }
  491. /**
  492. 处理文字教具键盘隐藏时,内容偏移。
  493. 可以
  494. @param n 键盘通知
  495. */
  496. - (void)keyboardDidDismiss:(NSNotification *)n
  497. {
  498. [self.boardView.scrollView setContentOffset:CGPointZero animated:YES];
  499. }
  500. #pragma mark - WhiteRoomCallbackDelegate
  501. - (void)firePhaseChanged:(WhiteRoomPhase)phase
  502. {
  503. NSLog(@"%s, %ld", __FUNCTION__, (long)phase);
  504. if (self.room.disconnectedBySelf || self.isReconnecting || !self.sdk) {
  505. return;
  506. }
  507. if (phase == WhiteRoomPhaseDisconnected && self.roomUuid && self.roomToken) {
  508. self.reconnecting = YES;
  509. WhiteRoomConfig *config = [[WhiteRoomConfig alloc] initWithUUID:self.roomUuid roomToken:self.roomToken uid:UserDefault(UIDKey)];
  510. [self.sdk joinRoomWithConfig:config callbacks:self.roomCallbackDelegate completionHandler:^(BOOL success, WhiteRoom * _Nullable room, NSError * _Nullable error) {
  511. self.reconnecting = NO;
  512. NSLog(@"reconnected");
  513. if (error) {
  514. NSLog(@"error:%@", [error localizedDescription]);
  515. dispatch_main_async_safe(^{
  516. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"白板连接失败"];
  517. self.isConnected = NO;
  518. });
  519. } else {
  520. self.room = room;
  521. self.isConnected = YES;
  522. }
  523. }];
  524. }
  525. }
  526. - (void)fireRoomStateChanged:(WhiteRoomState *)magixPhase;
  527. {
  528. NSLog(@"%s, %@", __func__, [magixPhase jsonString]);
  529. }
  530. - (void)fireBeingAbleToCommitChange:(BOOL)isAbleToCommit
  531. {
  532. NSLog(@"%s, %d", __func__, isAbleToCommit);
  533. }
  534. - (void)fireDisconnectWithError:(NSString *)error
  535. {
  536. dispatch_main_async_safe(^{
  537. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"白板断开连接"];
  538. self.isConnected = NO;
  539. });
  540. NSLog(@"%s, %@", __func__, error);
  541. }
  542. - (void)fireKickedWithReason:(NSString *)reason
  543. {
  544. NSLog(@"%s, %@", __func__, reason);
  545. }
  546. - (void)fireCatchErrorWhenAppendFrame:(NSUInteger)userId error:(NSString *)error
  547. {
  548. NSLog(@"%s, %lu %@", __func__, (unsigned long)userId, error);
  549. }
  550. - (void)fireMagixEvent:(WhiteEvent *)event
  551. {
  552. NSLog(@"fireMagixEvent: %@", [event jsonString]);
  553. }
  554. - (void)fireHighFrequencyEvent:(NSArray<WhiteEvent *>*)events
  555. {
  556. NSLog(@"%s", __func__);
  557. }
  558. - (NSMutableArray *)sceneArray
  559. {
  560. if (!_sceneArray) {
  561. _sceneArray = [NSMutableArray array];
  562. }
  563. return _sceneArray;
  564. }
  565. - (NSString *)whiteBoardPageName:(NSString *)name
  566. {
  567. return [NSString stringWithFormat:@"%@/%@", kSceneKey, name];
  568. }
  569. - (void)setRoomId:(NSString *)roomId {
  570. _roomId = roomId;
  571. // [self joinWhiteRoom];
  572. }
  573. // 图片选择回调
  574. - (void)evaluateWithChooseImage:(UIImage *)image {
  575. dispatch_async(dispatch_get_main_queue(), ^{
  576. [self uploadImage:image];
  577. });
  578. }
  579. - (void)uploadImage:(UIImage *)image {
  580. // 上传图片 回调显示
  581. // 如果需要旋转图片
  582. if (self.isRotationImage) {
  583. image = [UIImage rotationImage:image rotation:UIImageOrientationLeft];
  584. }
  585. NSData *imgData = [UIImage compressImage:image maxLength:5];
  586. [UPLOAD_MANAGER configWithfilePath:@"/whiteboard/"];
  587. [UPLOAD_MANAGER uploadImage:imgData fileName:@"classroomPic" successCallback:^(NSMutableArray * _Nonnull fileUrlArray) {
  588. NSString *imgUrl = [fileUrlArray lastObject];
  589. [self insertImageWithUrl:imgUrl size:image.size];
  590. } faliure:^(NSError * _Nullable error, NSString * _Nullable descMessaeg) {
  591. if (![NSString isEmptyString:descMessaeg]) {
  592. [LOADING_MANAGER MBShowAUTOHidingInWindow:descMessaeg];
  593. }
  594. }];
  595. }
  596. - (void)insertImageWithUrl:(NSString *)imageUrl size:(CGSize)imageSize {
  597. WhiteImageInformation *info = [[WhiteImageInformation alloc] init];
  598. info.width = imageSize.width;
  599. info.height = imageSize.height;
  600. info.uuid = self.roomUuid;
  601. [self.room insertImage:info src:imageUrl];
  602. }
  603. #pragma mark - TZImagePickerController
  604. - (void)pushImagePickerController {
  605. TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:MAXPHOTONUMBER columnNumber:COLUMNNUMBER delegate:self];
  606. #pragma mark - 四类个性化设置,这些参数都可以不传,此时会走默认设置
  607. imagePickerVc.isSelectOriginalPhoto = NO;
  608. // 1.设置目前已经选中的图片数组
  609. imagePickerVc.selectedAssets = self.imageAsset; // 目前已经选中的图片数组
  610. imagePickerVc.allowTakePicture = YES; // 在内部显示拍照按钮
  611. // 2. Set the appearance
  612. // 2. 在这里设置imagePickerVc的外观
  613. [imagePickerVc.navigationBar setBarTintColor:THEMECOLOR];
  614. // 3. Set allow picking video & photo & originalPhoto or not
  615. // 3. 设置是否可以选择视频/图片/原图
  616. imagePickerVc.allowPickingVideo = NO;
  617. imagePickerVc.allowPickingImage = YES;
  618. imagePickerVc.allowPickingOriginalPhoto = NO;
  619. imagePickerVc.allowCrop = NO;
  620. imagePickerVc.needCircleCrop = NO;
  621. imagePickerVc.showSelectBtn = YES;
  622. NSInteger left = 30;
  623. NSInteger widthHeight = kScreenWidth - 2 * left;
  624. NSInteger top = (kScreenHeight - widthHeight) / 2;
  625. imagePickerVc.cropRect = CGRectMake(left, top, widthHeight, widthHeight);
  626. // 4. 照片排列按修改时间升序
  627. imagePickerVc.sortAscendingByModificationDate = NO;
  628. #pragma mark - 到这里为止
  629. // You can get the photos by block, the same as by delegate.
  630. // 你可以通过block或者代理,来得到用户选择的照片.
  631. [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
  632. // 赋值
  633. [self evaluateWithChooseImage:[photos lastObject]];
  634. }];
  635. imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
  636. [[self getViewController] presentViewController:imagePickerVc animated:YES completion:nil];
  637. }
  638. #pragma mark - UIImagePickerController
  639. - (void)takePhoto {
  640. AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  641. if ((authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) && kiOS7Later) {
  642. [KSNormalAlertView ks_showAlertWithTitle:@"请在iPhone的""设置-隐私-相机""中允许访问相机" leftTitle:@"取消" rightTitle:@"设置" inView:self cancel:^{
  643. } confirm:^{
  644. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
  645. }];
  646. }
  647. else if (authStatus == AVAuthorizationStatusNotDetermined) {
  648. // 防止用户首次拍照拒绝授权时相机页黑屏
  649. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
  650. if (granted) {
  651. dispatch_async(dispatch_get_main_queue(), ^{
  652. [self takePhoto];
  653. });
  654. }
  655. }];
  656. }
  657. else if ([PHPhotoLibrary authorizationStatus] == 2) { // 已被拒绝,没有相册权限,将无法保存拍的照片
  658. [KSNormalAlertView ks_showAlertWithTitle:@"请在iPhone的""设置-隐私-相册""中允许访问相册" leftTitle:@"取消" rightTitle:@"设置" inView:self cancel:^{
  659. } confirm:^{
  660. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
  661. }];
  662. }
  663. else if ([PHPhotoLibrary authorizationStatus] == 0) { // 未请求过相册权限
  664. [[TZImageManager manager] requestAuthorizationWithCompletion:^{
  665. [self takePhoto];
  666. }];
  667. }
  668. else { // 调用相机
  669. UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
  670. if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
  671. self.imagePickerVc.sourceType = sourceType;
  672. if(kiOS8Later) {
  673. _imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
  674. }
  675. _imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
  676. [[self getViewController] presentViewController:_imagePickerVc animated:YES completion:nil];
  677. } else {
  678. NSLog(@"模拟器中无法打开照相机,请在真机中使用");
  679. }
  680. }
  681. }
  682. // 外部拍照进入的方法
  683. - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
  684. [picker dismissViewControllerAnimated:YES completion:nil];
  685. NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
  686. if ([type isEqualToString:@"public.image"]) {
  687. TZImagePickerController *tzImagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:MAXPHOTONUMBER delegate:self];
  688. tzImagePickerVc.allowCrop = YES;
  689. tzImagePickerVc.needCircleCrop = NO;
  690. tzImagePickerVc.showSelectBtn = YES;
  691. tzImagePickerVc.sortAscendingByModificationDate = NO;
  692. [tzImagePickerVc showProgressHUD];
  693. UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
  694. // save photo and get asset / 保存图片,获取到asset
  695. [[TZImageManager manager] savePhotoWithImage:image completion:^(PHAsset *asset,NSError *error){
  696. if (error) { // 如果保存失败,基本是没有相册权限导致的...
  697. [tzImagePickerVc hideProgressHUD];
  698. NSLog(@"图片保存失败 %@",error);
  699. [KSNormalAlertView ks_showAlertWithTitle:@"请在iPhone的""设置-隐私-相册""中允许访问相册" leftTitle:@"取消" rightTitle:@"设置" inView:self cancel:^{
  700. } confirm:^{
  701. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
  702. }];
  703. } else {
  704. TZAssetModel *assetModel = [[TZImageManager manager] createModelWithAsset:asset];
  705. TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initCropTypeWithAsset:assetModel.asset photo:image completion:^(UIImage *cropImage, id asset) {
  706. // 回调
  707. [self evaluateWithChooseImage:cropImage];
  708. }];
  709. imagePicker.allowPickingImage = YES;
  710. imagePicker.allowCrop = YES;
  711. imagePicker.needCircleCrop = NO;
  712. NSInteger left = 30;
  713. NSInteger widthHeight = kScreenWidth - 2 * left;
  714. NSInteger top = (kScreenHeight - widthHeight) / 2;
  715. imagePicker.cropRect = CGRectMake(left, top, widthHeight, widthHeight);
  716. imagePicker.modalPresentationStyle = UIModalPresentationFullScreen;
  717. [[self getViewController] presentViewController:imagePicker animated:YES completion:nil];
  718. }
  719. }];
  720. }
  721. }
  722. - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
  723. if ([picker isKindOfClass:[UIImagePickerController class]]) {
  724. [picker dismissViewControllerAnimated:YES completion:nil];
  725. }
  726. }
  727. #pragma mark - getter
  728. - (NSMutableArray *)imageArray {
  729. if (!_imageArray) {
  730. _imageArray = [NSMutableArray array];
  731. }
  732. return _imageArray;
  733. }
  734. - (NSMutableArray *)imageAsset {
  735. if (!_imageAsset) {
  736. _imageAsset = [NSMutableArray array];
  737. }
  738. return _imageAsset;
  739. }
  740. - (UIImagePickerController *)imagePickerVc {
  741. if (_imagePickerVc == nil) {
  742. _imagePickerVc = [[UIImagePickerController alloc] init];
  743. _imagePickerVc.delegate = self;
  744. // set appearance / 改变相册选择页的导航栏外观
  745. // _imagePickerVc.navigationBar.barTintColor = THEMECOLOR;
  746. // _imagePickerVc.navigationBar.tintColor = THEMECOLOR;
  747. UIBarButtonItem *tzBarItem, *BarItem;
  748. if (kiOS9Later) {
  749. tzBarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]];
  750. BarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UIImagePickerController class]]];
  751. } else {
  752. #pragma clang diagnostic push
  753. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  754. tzBarItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil];
  755. BarItem = [UIBarButtonItem appearanceWhenContainedIn:[UIImagePickerController class], nil];
  756. #pragma clang diagnostic pop
  757. }
  758. NSDictionary *titleTextAttributes = [tzBarItem titleTextAttributesForState:UIControlStateNormal];
  759. [BarItem setTitleTextAttributes:titleTextAttributes forState:UIControlStateNormal];
  760. }
  761. return _imagePickerVc;
  762. }
  763. - (UIViewController *)getViewController {
  764. for (UIView *view = self; view; view = view.superview) {
  765. UIResponder *nextResponder = [view nextResponder];
  766. if ([nextResponder isKindOfClass:[UIViewController class]]) {
  767. return (UIViewController *)nextResponder;
  768. }
  769. }
  770. return nil;
  771. }
  772. /*
  773. // Only override drawRect: if you perform custom drawing.
  774. // An empty implementation adversely affects performance during animation.
  775. - (void)drawRect:(CGRect)rect {
  776. // Drawing code
  777. }
  778. */
  779. @end