KSNewWhiteBoard.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. //
  2. // KSNewWhiteBoard.m
  3. // TeacherDaya
  4. //
  5. // Created by 王智 on 2022/8/8.
  6. // Copyright © 2022 DayaMusic. All rights reserved.
  7. //
  8. #import "KSNewWhiteBoard.h"
  9. #import <KSToolLibrary/WeakWebViewScriptMessageDelegate.h>
  10. #import <WebKit/WebKit.h>
  11. #import "CustomNavViewController.h"
  12. #define MAXPHOTONUMBER (1)
  13. #define COLUMNNUMBER (3)
  14. #import "TZImageManager.h"
  15. #import <AssetsLibrary/AssetsLibrary.h>
  16. #import <Photos/Photos.h>
  17. #import "TZVideoPlayerController.h"
  18. #import "TZImagePickerController.h"
  19. #import <KSToolLibrary/UIImage+ResizeImage.h>
  20. #import <KSToolLibrary/UIImage+Color.h>
  21. #import "WhiteUtils.h"
  22. #import "WebViewBaseConfig.h"
  23. #define touchPy 10
  24. @interface KSNewWhiteBoard ()<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler,UIImagePickerControllerDelegate,UINavigationControllerDelegate,TZImagePickerControllerDelegate,UIAlertViewDelegate,UIGestureRecognizerDelegate>
  25. {
  26. BOOL _isSelectOriginalPhoto;
  27. }
  28. @property (nonatomic, strong, nullable) WKWebView *myWebView;
  29. @property (nonatomic, assign) BOOL hasModify;
  30. @property (nonatomic, strong) UIImagePickerController *imagePickerVc;
  31. @property (nonatomic, strong) NSMutableArray *imageArray; // 图片数组
  32. @property (nonatomic, strong) NSMutableArray *imageAsset; // 图片 asset
  33. @property (nonatomic, strong) UIImageView *chooseImage;
  34. @property (nonatomic, assign) CGPoint beginPoint;
  35. @property (nonatomic, weak) id<KSNewWhiteboardViewDelegate> delegate;
  36. @property (nonatomic, assign) BOOL isChoosePhoto;
  37. @property (nonatomic, assign) BOOL isLoadingSource;
  38. @property (nonatomic, assign) BOOL isJoinRoom;
  39. @end
  40. @implementation KSNewWhiteBoard
  41. - (instancetype)initWithDelegate:(id<KSNewWhiteboardViewDelegate>)delegate frame:(CGRect)frame {
  42. self = [super initWithFrame:frame];
  43. if (self) {
  44. self.delegate = delegate;
  45. }
  46. return self;
  47. }
  48. - (void)configView:(CGRect)frame {
  49. [self addSubview:self.myWebView];
  50. self.myWebView.userInteractionEnabled = NO;
  51. [self.myWebView mas_makeConstraints:^(MASConstraintMaker *make) {
  52. make.left.right.top.bottom.mas_equalTo(self);
  53. }];
  54. [self addSubview:self.refreshButton];
  55. [self.refreshButton mas_makeConstraints:^(MASConstraintMaker *make) {
  56. make.left.mas_equalTo(self.myWebView.mas_left).offset(18);
  57. make.top.mas_equalTo(self.myWebView.mas_top).offset(10);
  58. make.width.mas_equalTo(81);
  59. make.height.mas_equalTo(21);
  60. }];
  61. }
  62. - (void)configUserAgent:(WKWebViewConfiguration *)config {
  63. NSString *oldUserAgent = config.applicationNameForUserAgent;
  64. NSString *newAgent = [NSString stringWithFormat:@"%@ %@ %@",oldUserAgent,AGENT_NAME,AGENT_DOMAIN];
  65. config.applicationNameForUserAgent = newAgent;
  66. }
  67. - (void)loadRequest {
  68. NSLog(@"---------- %@",self.url);
  69. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0f];
  70. [self.myWebView loadRequest:request];
  71. }
  72. - (WKWebView *)myWebView {
  73. if (!_myWebView) {
  74. _myWebView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:[self getwebConfig]];
  75. _myWebView.scrollView.bounces = NO;
  76. _myWebView.UIDelegate = self;
  77. _myWebView.navigationDelegate = self;
  78. #ifdef DEBUG
  79. if (@available(iOS 16.4, *)) {
  80. _myWebView.inspectable = YES;
  81. }
  82. #endif
  83. }
  84. return _myWebView;
  85. }
  86. - (WKWebViewConfiguration *)getwebConfig {
  87. WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
  88. config.selectionGranularity = WKSelectionGranularityDynamic;
  89. config.allowsInlineMediaPlayback = YES;
  90. if (@available(iOS 10.0, *)) {
  91. config.mediaTypesRequiringUserActionForPlayback = NO;
  92. } else {
  93. // Fallback on earlier versions
  94. config.requiresUserActionForMediaPlayback = NO;
  95. }
  96. config.processPool = [KSNewWhiteBoard singleWkProcessPool];
  97. config.websiteDataStore = [WKWebsiteDataStore defaultDataStore];
  98. [self configUserAgent:config];
  99. //自定义的WKScriptMessageHandler 是为了解决内存不释放的问题
  100. WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
  101. //这个类主要用来做native与JavaScript的交互管理
  102. WKUserContentController * wkUController = [[WKUserContentController alloc] init];
  103. [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:SCRIPT_NAME];
  104. config.userContentController = wkUController;
  105. WKPreferences *preferences = [WKPreferences new];
  106. // 是否支出javaScript
  107. preferences.javaScriptEnabled = YES;
  108. //不通过用户交互,是否可以打开窗口
  109. preferences.javaScriptCanOpenWindowsAutomatically = YES;
  110. config.preferences = preferences;
  111. return config;
  112. }
  113. + (WKProcessPool*)singleWkProcessPool {
  114. static WKProcessPool *sharedPool;
  115. static dispatch_once_t onceToken;
  116. dispatch_once(&onceToken, ^{
  117. sharedPool = [[WKProcessPool alloc] init];
  118. });
  119. return sharedPool;
  120. }
  121. // 调用js方法
  122. - (void)postMessageJS:(NSDictionary *)jsDict {
  123. dispatch_async(dispatch_get_main_queue(), ^{
  124. NSString *jsString = [jsDict mj_JSONString];
  125. [self.myWebView evaluateJavaScript:[NSString stringWithFormat:@"postMessage(%@,'*')", jsString] completionHandler:nil];
  126. });
  127. }
  128. #pragma mark ----- WKWebView delegate
  129. // 1 在发送请求之前,决定是否跳转
  130. - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
  131. NSLog(@"1-------在发送请求之前,决定是否跳转 -->%@",navigationAction.request);
  132. NSURL *url = navigationAction.request.URL;
  133. NSString *scheme = [url scheme];
  134. UIApplication *app = [UIApplication sharedApplication];
  135. NSString *urlString = url.absoluteString;
  136. // 打电话
  137. if ([scheme isEqualToString:@"tel"]) {
  138. if ([app canOpenURL:url]) {
  139. [app openURL:url];
  140. // 一定要加上这句,否则会打开新页面
  141. decisionHandler(WKNavigationActionPolicyCancel);
  142. return;
  143. }
  144. }
  145. decisionHandler(WKNavigationActionPolicyAllow);
  146. }
  147. // 2 页面开始加载时调用
  148. - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
  149. NSLog(@"2-------页面开始加载时调用");
  150. }
  151. // 3 在收到响应后,决定是否跳转
  152. - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
  153. /// 在收到服务器的响应头,根据response相关信息,决定是否跳转。decisionHandler必须调用,来决定是否跳转,参数WKNavigationActionPolicyCancel取消跳转,WKNavigationActionPolicyAllow允许跳转
  154. NSLog(@"3-------在收到响应后,决定是否跳转");
  155. decisionHandler(WKNavigationResponsePolicyAllow);
  156. }
  157. // 4 当内容开始返回时调用
  158. - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
  159. NSLog(@"4-------当内容开始返回时调用");
  160. }
  161. // 5 页面加载完成之后调用
  162. - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
  163. NSLog(@"5-------页面加载完成之后调用");
  164. if (_hasModify == NO) {
  165. [self configLocalStorage];
  166. }
  167. }
  168. - (void)configLocalStorage {
  169. _hasModify = YES;
  170. NSString *jsString = [NSString stringWithFormat:@"localStorage.setItem('Authorization', '%@ %@')",UserDefault(Token_type), UserDefault(TokenKey)];
  171. [self.myWebView evaluateJavaScript:jsString completionHandler:nil];
  172. }
  173. // 6 页面加载失败时调用
  174. - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
  175. NSLog(@"6-------页面加载失败时调用");
  176. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"网页加载失败!"];
  177. }
  178. // 接收到服务器跳转请求之后调用
  179. - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
  180. NSLog(@"-------接收到服务器跳转请求之后调用");
  181. }
  182. // 数据加载发生错误时调用
  183. - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
  184. NSLog(@"----数据加载发生错误时调用");
  185. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"数据加载发生错误!"];
  186. }
  187. - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *_Nullable))completionHandler
  188. {
  189. dispatch_queue_t queue = dispatch_queue_create("webViewChallengeQueue", NULL);
  190. dispatch_async(queue, ^{
  191. if (SSL_AUTH) {
  192. NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
  193. NSURLCredential *customCredential = nil;
  194. if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
  195. // 默认信任
  196. customCredential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
  197. disposition = NSURLSessionAuthChallengeUseCredential;
  198. }
  199. else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
  200. // client authentication
  201. SecIdentityRef identity = NULL;
  202. SecTrustRef trust = NULL;
  203. if ([AuthChallengeManager extractIdentity:&identity andTrust:&trust filePath:CERT_PATH]) {
  204. SecCertificateRef certificate = NULL;
  205. SecIdentityCopyCertificate(identity, &certificate);
  206. const void*certs[] = {certificate};
  207. CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
  208. customCredential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
  209. disposition = NSURLSessionAuthChallengeUseCredential;
  210. }
  211. }
  212. if (completionHandler) {
  213. completionHandler(disposition, customCredential);
  214. }
  215. }
  216. else {
  217. if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
  218. if (challenge.previousFailureCount == 0) {
  219. NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
  220. completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
  221. } else {
  222. completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
  223. }
  224. }
  225. else {
  226. completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
  227. }
  228. }
  229. });
  230. }
  231. #pragma mark - WKScriptMessageHandler
  232. - (void)userContentController:(WKUserContentController *)userContentController
  233. didReceiveScriptMessage:(WKScriptMessage *)message {
  234. if ([message.name isEqualToString:SCRIPT_NAME]) {
  235. NSDictionary *parm = [self convertJsonStringToNSDictionary:message.body];
  236. // 回到主线程
  237. dispatch_async(dispatch_get_main_queue(), ^{
  238. [self handleScriptMessageSource:parm];
  239. });
  240. }
  241. }
  242. - (void)handleScriptMessageSource:(NSDictionary *)parm {
  243. NSLog(@"----%@",parm);
  244. if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"whiteboardSelectPhoto"]) { // 选择图片
  245. self.isChoosePhoto = YES;
  246. [self pushImagePickerController];
  247. }
  248. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"whiteboardSelectMusicLibrary"]) { // 选择曲库
  249. // [self chooseImageWithLibiary];
  250. }
  251. // 白板连接状态
  252. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"whiteboardConnectStatus"]) {
  253. NSDictionary *valueDic = [parm ks_dictionaryValueForKey:@"content"];
  254. if ([[valueDic ks_stringValueForKey:@"status"] isEqualToString:@"success"]) { // 连接成功
  255. self.connectedSuccess = YES;
  256. }
  257. else { // 连接失败
  258. self.connectedSuccess = NO;
  259. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"白板连接错误,请刷新一下"];
  260. }
  261. }
  262. }
  263. - (NSDictionary *)convertJsonStringToNSDictionary:(NSString *)jsonString {
  264. if (jsonString == nil) {
  265. return nil;
  266. }
  267. NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
  268. NSError *error;
  269. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
  270. if (error) {
  271. NSLog(@"jsonString解析失败:%@", error);
  272. return nil;
  273. }
  274. return json;
  275. }
  276. - (void)dealloc {
  277. NSLog(@"-white board-");
  278. [[_myWebView configuration].userContentController removeScriptMessageHandlerForName:SCRIPT_NAME];
  279. [_myWebView loadHTMLString:@"" baseURL:nil];
  280. [_myWebView removeFromSuperview];
  281. _myWebView = nil;
  282. [[NSURLCache sharedURLCache] removeAllCachedResponses];
  283. [[NSURLCache sharedURLCache] setDiskCapacity:0];
  284. [[NSURLCache sharedURLCache] setMemoryCapacity:0];
  285. [[NSNotificationCenter defaultCenter] removeObserver:self];
  286. }
  287. #pragma mark ------- 曲库下载或者相册选择
  288. - (void)uploadImage {
  289. UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:IS_IPAD ? UIAlertControllerStyleAlert : UIAlertControllerStyleActionSheet];
  290. [alertVC addAction:[UIAlertAction actionWithTitle:@"曲库选择" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  291. [self chooseImageWithLibiary];
  292. }]];
  293. [alertVC addAction:[UIAlertAction actionWithTitle:@"从手机相册选择" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  294. // 调用相册
  295. [self pushImagePickerController];
  296. }]];
  297. [alertVC addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  298. }]];
  299. alertVC.modalPresentationStyle = UIModalPresentationFullScreen;
  300. [[self getViewController] presentViewController:alertVC animated:true completion:nil];
  301. }
  302. - (void)chooseImageWithLibiary {
  303. }
  304. #pragma mark - TZImagePickerController
  305. - (void)pushImagePickerController {
  306. TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:MAXPHOTONUMBER columnNumber:COLUMNNUMBER delegate:self];
  307. #pragma mark - 四类个性化设置,这些参数都可以不传,此时会走默认设置
  308. imagePickerVc.isSelectOriginalPhoto = NO;
  309. // 1.设置目前已经选中的图片数组
  310. imagePickerVc.selectedAssets = self.imageAsset; // 目前已经选中的图片数组
  311. imagePickerVc.allowTakePicture = YES; // 在内部显示拍照按钮
  312. // 2. Set the appearance
  313. // 2. 在这里设置imagePickerVc的外观
  314. [imagePickerVc.navigationBar setBarTintColor:THEMECOLOR];
  315. // 3. Set allow picking video & photo & originalPhoto or not
  316. // 3. 设置是否可以选择视频/图片/原图
  317. imagePickerVc.allowPickingVideo = NO;
  318. imagePickerVc.allowPickingImage = YES;
  319. imagePickerVc.allowPickingOriginalPhoto = NO;
  320. imagePickerVc.allowCrop = NO;
  321. imagePickerVc.needCircleCrop = NO;
  322. imagePickerVc.showSelectBtn = YES;
  323. NSInteger left = 30;
  324. NSInteger widthHeight = kScreenWidth - 2 * left;
  325. NSInteger top = (kScreenHeight - widthHeight) / 2;
  326. imagePickerVc.cropRect = CGRectMake(left, top, widthHeight, widthHeight);
  327. // 4. 照片排列按修改时间升序
  328. imagePickerVc.sortAscendingByModificationDate = NO;
  329. #pragma mark - 到这里为止
  330. // You can get the photos by block, the same as by delegate.
  331. // 你可以通过block或者代理,来得到用户选择的照片.
  332. [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
  333. // 赋值
  334. [self evaluateWithChooseImage:[photos lastObject] scaleWidth:NO];
  335. }];
  336. imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
  337. [[self getViewController] presentViewController:imagePickerVc animated:YES completion:nil];
  338. }
  339. // 图片选择回调
  340. - (void)evaluateWithChooseImage:(UIImage *)image scaleWidth:(BOOL)needScaleWidth {
  341. dispatch_async(dispatch_get_main_queue(), ^{
  342. [self uploadImage:image scaleWidth:needScaleWidth];
  343. });
  344. }
  345. - (void)uploadImage:(UIImage *)image scaleWidth:(BOOL)needScaleWidth {
  346. // 上传图片 回调显示
  347. NSData *imgData = [UIImage compressImage:image maxLength:5];
  348. NSString *fileName = @"whiteboardImage";
  349. [UPLOAD_MANAGER configWithfilePath:@"/whiteboard/"];
  350. [UPLOAD_MANAGER uploadImage:imgData fileName:fileName successCallback:^(NSMutableArray * _Nonnull fileUrlArray) {
  351. NSString *imageUrl = [fileUrlArray lastObject];
  352. [self insertImageWithUrl:imageUrl size:image.size];
  353. } faliure:^(NSError * _Nullable error, NSString * _Nullable descMessaeg) {
  354. if (![NSString isEmptyString:descMessaeg]) {
  355. [LOADING_MANAGER MBShowAUTOHidingInWindow:descMessaeg];
  356. }
  357. }];
  358. }
  359. - (void)insertImageWithUrl:(NSString *)imageUrl size:(CGSize)imageSize {
  360. NSMutableDictionary *parm = [NSMutableDictionary dictionary];
  361. if (_isChoosePhoto) {
  362. self.isChoosePhoto = NO;
  363. [parm setValue:@"whiteboardSelectPhoto" forKey:@"api"];
  364. }
  365. else {
  366. [parm setValue:@"whiteboardSelectMusicLibrary" forKey:@"api"];
  367. }
  368. NSMutableDictionary *content = [NSMutableDictionary dictionary];
  369. [content setValue:imageUrl forKey:@"url"];
  370. [parm setValue:content forKey:@"content"];
  371. [self postMessageJS:parm];
  372. }
  373. #pragma mark - UIImagePickerController
  374. - (void)takePhoto {
  375. AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  376. if ((authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) && kiOS7Later) {
  377. // 无权限 做一个友好的提示
  378. #pragma clang diagnostic push
  379. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  380. UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"无法使用相机" message:@"请在iPhone的""设置-隐私-相机""中允许访问相机" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"设置", nil];
  381. [alert show];
  382. #pragma clang diagnostic pop
  383. }
  384. else if (authStatus == AVAuthorizationStatusNotDetermined) {
  385. // 防止用户首次拍照拒绝授权时相机页黑屏
  386. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
  387. if (granted) {
  388. dispatch_async(dispatch_get_main_queue(), ^{
  389. [self takePhoto];
  390. });
  391. }
  392. }];
  393. }
  394. else if ([PHPhotoLibrary authorizationStatus] == 2) { // 已被拒绝,没有相册权限,将无法保存拍的照片
  395. #pragma clang diagnostic push
  396. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  397. UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"无法访问相册" message:@"请在iPhone的""设置-隐私-相册""中允许访问相册" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"设置", nil];
  398. [alert show];
  399. #pragma clang diagnostic pop
  400. }
  401. else if ([PHPhotoLibrary authorizationStatus] == 0) { // 未请求过相册权限
  402. [[TZImageManager manager] requestAuthorizationWithCompletion:^{
  403. [self takePhoto];
  404. }];
  405. }
  406. else { // 调用相机
  407. UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
  408. if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
  409. self.imagePickerVc.sourceType = sourceType;
  410. if(kiOS8Later) {
  411. _imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
  412. }
  413. _imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
  414. [[self getViewController] presentViewController:_imagePickerVc animated:YES completion:nil];
  415. } else {
  416. NSLog(@"模拟器中无法打开照相机,请在真机中使用");
  417. }
  418. }
  419. }
  420. // 外部拍照进入的方法
  421. - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
  422. [picker dismissViewControllerAnimated:YES completion:nil];
  423. NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
  424. if ([type isEqualToString:@"public.image"]) {
  425. TZImagePickerController *tzImagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:MAXPHOTONUMBER delegate:self];
  426. tzImagePickerVc.allowCrop = YES;
  427. tzImagePickerVc.needCircleCrop = NO;
  428. tzImagePickerVc.showSelectBtn = YES;
  429. tzImagePickerVc.sortAscendingByModificationDate = NO;
  430. [tzImagePickerVc showProgressHUD];
  431. UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
  432. // save photo and get asset / 保存图片,获取到asset
  433. [[TZImageManager manager] savePhotoWithImage:image completion:^(PHAsset *asset,NSError *error){
  434. if (error) { // 如果保存失败,基本是没有相册权限导致的...
  435. [tzImagePickerVc hideProgressHUD];
  436. NSLog(@"图片保存失败 %@",error);
  437. UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"无法保存图片" message:@"请在iPhone的""设置-隐私-相册""中允许访问相册" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"设置", nil];
  438. alert.tag = 1;
  439. [alert show];
  440. } else {
  441. TZAssetModel *assetModel = [[TZImageManager manager] createModelWithAsset:asset];
  442. TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initCropTypeWithAsset:assetModel.asset photo:image completion:^(UIImage *cropImage, id asset) {
  443. // 回调
  444. [self evaluateWithChooseImage:cropImage scaleWidth:NO];
  445. }];
  446. imagePicker.allowPickingImage = YES;
  447. imagePicker.allowCrop = YES;
  448. imagePicker.needCircleCrop = NO;
  449. NSInteger left = 30;
  450. NSInteger widthHeight = kScreenWidth - 2 * left;
  451. NSInteger top = (kScreenHeight - widthHeight) / 2;
  452. imagePicker.cropRect = CGRectMake(left, top, widthHeight, widthHeight);
  453. imagePicker.modalPresentationStyle = UIModalPresentationFullScreen;
  454. [[self getViewController] presentViewController:imagePicker animated:YES completion:nil];
  455. }
  456. }];
  457. }
  458. }
  459. - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
  460. if ([picker isKindOfClass:[UIImagePickerController class]]) {
  461. [picker dismissViewControllerAnimated:YES completion:nil];
  462. }
  463. }
  464. #pragma mark - getter
  465. - (NSMutableArray *)imageArray {
  466. if (!_imageArray) {
  467. _imageArray = [NSMutableArray array];
  468. }
  469. return _imageArray;
  470. }
  471. - (NSMutableArray *)imageAsset {
  472. if (!_imageAsset) {
  473. _imageAsset = [NSMutableArray array];
  474. }
  475. return _imageAsset;
  476. }
  477. - (UIImagePickerController *)imagePickerVc {
  478. if (_imagePickerVc == nil) {
  479. _imagePickerVc = [[UIImagePickerController alloc] init];
  480. _imagePickerVc.delegate = self;
  481. // set appearance / 改变相册选择页的导航栏外观
  482. _imagePickerVc.navigationBar.barTintColor = THEMECOLOR;
  483. _imagePickerVc.navigationBar.tintColor = THEMECOLOR;
  484. UIBarButtonItem *tzBarItem, *BarItem;
  485. if (kiOS9Later) {
  486. tzBarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]];
  487. BarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UIImagePickerController class]]];
  488. } else {
  489. #pragma clang diagnostic push
  490. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  491. tzBarItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil];
  492. BarItem = [UIBarButtonItem appearanceWhenContainedIn:[UIImagePickerController class], nil];
  493. #pragma clang diagnostic pop
  494. }
  495. NSDictionary *titleTextAttributes = [tzBarItem titleTextAttributesForState:UIControlStateNormal];
  496. [BarItem setTitleTextAttributes:titleTextAttributes forState:UIControlStateNormal];
  497. }
  498. return _imagePickerVc;
  499. }
  500. #pragma mark - UIAlertViewDelegate
  501. #pragma clang diagnostic push
  502. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  503. #pragma clang diagnostic ignored "-Wdeprecated-implementations"
  504. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
  505. if (buttonIndex == 1) { // 去设置界面,开启相机访问权限
  506. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
  507. }
  508. }
  509. #pragma clang diagnostic pop
  510. - (UIImageView *)chooseImage {
  511. if (!_chooseImage) {
  512. _chooseImage = [[UIImageView alloc] init];
  513. }
  514. return _chooseImage;
  515. }
  516. /*
  517. // Only override drawRect: if you perform custom drawing.
  518. // An empty implementation adversely affects performance during animation.
  519. - (void)drawRect:(CGRect)rect {
  520. // Drawing code
  521. }
  522. */
  523. - (void)setCurrentFrame:(CGRect)currentFrame {
  524. _currentFrame = currentFrame;
  525. self.frame = currentFrame;
  526. }
  527. - (void)destroy {
  528. }
  529. // 加入白板房间
  530. - (void)joinWhiteRoom {
  531. [self configView:self.bounds];
  532. if (self.url) {
  533. [self loadRequest];
  534. }
  535. else {
  536. self.isJoinRoom = YES;
  537. [LOADING_MANAGER showHUD];
  538. }
  539. }
  540. // 离开房间
  541. - (void)leaveRoom {
  542. self.isJoinRoom = NO;
  543. [self removeLoadig];
  544. [[_myWebView configuration].userContentController removeScriptMessageHandlerForName:SCRIPT_NAME];
  545. [_myWebView loadHTMLString:@"" baseURL:nil];
  546. [_myWebView removeFromSuperview];
  547. _myWebView = nil;
  548. [[NSURLCache sharedURLCache] removeAllCachedResponses];
  549. [[NSURLCache sharedURLCache] setDiskCapacity:0];
  550. [[NSURLCache sharedURLCache] setMemoryCapacity:0];
  551. [[NSNotificationCenter defaultCenter] removeObserver:self];
  552. }
  553. - (void)showLoading {
  554. self.isLoadingSource = YES;
  555. }
  556. - (void)removeLoadig {
  557. self.isLoadingSource = NO;
  558. if (self.isJoinRoom) {
  559. [LOADING_MANAGER removeHUD];
  560. [self loadRequest];
  561. }
  562. }
  563. - (void)setConnectedSuccess:(BOOL)connectedSuccess {
  564. _connectedSuccess = connectedSuccess;
  565. if (connectedSuccess) { // 连接成功
  566. self.refreshButton.hidden = YES;
  567. }
  568. else { // 连接失败
  569. self.refreshButton.hidden = NO;
  570. }
  571. }
  572. - (KSWhiteboardRefreshView *)refreshButton {
  573. if (!_refreshButton) {
  574. _refreshButton = [KSWhiteboardRefreshView shareInstance];
  575. MJWeakSelf;
  576. [_refreshButton refreshActionCallback:^{
  577. [weakSelf refreshWebViewAction];
  578. }];
  579. }
  580. return _refreshButton;
  581. }
  582. - (void)refreshWebViewAction {
  583. // 如果链接为空
  584. if ([NSString isEmptyString:self.url]) {
  585. [LOADING_MANAGER showHUD];
  586. [WhiteUtils getRoomTokenWithRoomId:self.roomId completionHandler:^(NSString * _Nullable uuid, NSString * _Nullable roomToken, NSString * _Nonnull randomRoom, NSString * _Nullable randomNumeric, NSError * _Nullable error) {
  587. [LOADING_MANAGER removeHUD];
  588. if (!error) {
  589. NSString *url = [NSString stringWithFormat:@"%@%@?roomId=%@&userId=%@&clientType=STUDENT&role=viewer#room=%@,%@", hostURL, @"/whiteboard/",self.roomId,UserDefault(UIDKey),randomRoom,randomNumeric];
  590. self.url = url;
  591. [self refreshRequest];
  592. }
  593. }];
  594. }
  595. else { // 刷新webView
  596. [self refreshRequest];
  597. }
  598. }
  599. - (void)refreshRequest {
  600. [LOADING_MANAGER showHUD];
  601. [self loadRequest];
  602. [LOADING_MANAGER removeHUD];
  603. }
  604. - (UIViewController *)getViewController {
  605. for (UIView *view = self; view; view = view.superview) {
  606. UIResponder *nextResponder = [view nextResponder];
  607. if ([nextResponder isKindOfClass:[UIViewController class]]) {
  608. return (UIViewController *)nextResponder;
  609. }
  610. }
  611. return nil;
  612. }
  613. @end