| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- //
- // FilePreviewer.m
- // AIIM
- //
- // Created by qitewei on 2025/5/14.
- //
- #import "FilePreviewer.h"
- #import <AVKit/AVKit.h>
- #import <WebKit/WebKit.h>
- #import "ZoomableImageView.h"
- @interface FilePreviewer()<UIDocumentInteractionControllerDelegate>
- @property (nonatomic, strong) UIDocumentInteractionController *documentController;
- @property (nonatomic, strong) UIViewController *webPreviewController;
- @property (nonatomic, weak) UIViewController *currentParentViewController;
- @property (nonatomic, strong) UIActivityIndicatorView *loadingIndicator;
- @property (nonatomic, strong) UIImage *showimge;
- @end
- @implementation FilePreviewer
- + (instancetype)shared {
- static FilePreviewer *instance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- instance = [[FilePreviewer alloc] init];
- // 默认图片下载选项
- instance.imageDownloadOptions = SDWebImageRetryFailed | SDWebImageHighPriority;
- // 注册WebP解码器(如果需要支持WebP格式)
- [SDImageCodersManager.sharedManager addCoder:SDImageAWebPCoder.sharedCoder];
- });
- return instance;
- }
- #pragma mark - Public Methods
- - (void)previewFileWithLocalPath:(NSString *)localPath
- remoteURL:(NSURL *)remoteURL
- fromViewController:(UIViewController *)parentViewController {
-
- self.currentParentViewController = parentViewController;
- // 优先尝试本地路径
- if (localPath && [[NSFileManager defaultManager] fileExistsAtPath:localPath]) {
- NSLog(@"本地路径");
- [self previewFileAtPath:localPath];
- }
- // 如果本地路径不存在或无效,尝试远程URL
- else if (remoteURL) {
- // [self showLoadingIndicator];
- NSLog(@"尝试远程URL");
- [self previewRemoteFileAtURL:remoteURL];
- }
- else {
- [MBProgressHUD showWithText:@"文件加载中"];
- // [self showErrorAlert:@"文件不存在或无法访问"];
- }
- }
- #pragma mark - Private Methods - Preview
- - (void)previewFileAtPath:(NSString *)filePath {
- NSURL *fileURL = [NSURL fileURLWithPath:filePath];
- NSString *fileExtension = [[filePath pathExtension] lowercaseString];
-
- if ([self isImageFile:fileExtension]) {
- [self previewImageAtPath:filePath];
- } else if ([self isVideoFile:fileExtension]) {
- [self previewVideoAtURL:fileURL];
- } else {
- [self previewDocumentAtURL:fileURL];
- }
- }
- - (void)previewRemoteFileAtURL:(NSURL *)url {
- if (!url || ![url scheme]) {
- // [self showErrorAlert:@"文件加载中"];
- [MBProgressHUD showWithText:@"文件加载中"];
- return;
- }
-
- NSString *fileExtension = [[url pathExtension] lowercaseString];
-
- if ([self isImageFile:fileExtension]) {
- [self previewRemoteImageWithURL:url];
- } else if ([self isVideoFile:fileExtension]) {
- [self previewVideoAtURL:url];
- } else {
- [self previewWithWebView:url];
- }
- }
- #pragma mark - Private Methods - Image Preview
- // 预览本地图片
- - (void)previewImageAtPath:(NSString *)imagePath {
- NSURL *imageURL = [NSURL fileURLWithPath:imagePath];
-
- // 使用SDWebImage加载本地图片(支持WebP等格式)
- [self showImagePreviewWithURL:imageURL];
- }
- // 预览远程图片
- - (void)previewRemoteImageWithURL:(NSURL *)imageURL {
- [self showImagePreviewWithURL:imageURL];
- }
- // 通用图片预览方法
- - (void)showImagePreviewWithURL:(NSURL *)imageURL {
- UIViewController *previewController = [[UIViewController alloc] init];
- previewController.view.backgroundColor = [UIColor blackColor];
-
- // 使用自定义的可缩放图片视图
- ZoomableImageView *zoomableView = [[ZoomableImageView alloc] initWithFrame:previewController.view.bounds];
- zoomableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- // zoomableView.imageView.contentMode = UIViewContentModeScaleAspectFit;
- [previewController.view addSubview:zoomableView];
-
- // 添加关闭按钮
- UIButton *closeButton = [UIButton buttonWithType:UIButtonTypeSystem];
- // [closeButton setBackgroundImage:kImageMake(@"common_close_white") forState:UIControlStateNormal];
- [closeButton setBackgroundColor:globalColor(GCTypeDark1)];
- [closeButton setImage:kImageMake(@"common_close_white") forState:UIControlStateNormal];
-
- closeButton.frame = CGRectMake(20, 60, 40, 40);
- closeButton.layer.cornerRadius = 5;
- closeButton.tag = 100; // 用于稍后隐藏/显示
- [closeButton addTarget:self action:@selector(dismissPreviewController) forControlEvents:UIControlEventTouchUpInside];
- [previewController.view addSubview:closeButton];
-
-
-
- // 添加保存 本地按钮
- UIButton *saveButton = [UIButton buttonWithType:UIButtonTypeSystem];
- [saveButton setBackgroundColor:globalColor(GCTypeDark1)];
- [saveButton setImage:kImageMake(@"xiazaibendi") forState:UIControlStateNormal];
- CGFloat width = previewController.view.frame.size.width;
- saveButton.frame = CGRectMake(width-60, 60, 40, 40);
- saveButton.layer.cornerRadius = 5;
- saveButton.tag = 101; // 用于稍后隐藏/显示
- [saveButton addTarget:self action:@selector(saveImg) forControlEvents:UIControlEventTouchUpInside];
- [previewController.view addSubview:saveButton];
-
-
-
- // 添加加载指示器
- UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
- indicator.center = previewController.view.center;
- [indicator startAnimating];
- [previewController.view addSubview:indicator];
-
- [self presentViewController:previewController];
- self.webPreviewController = previewController;
-
- __weak typeof(self) weakSelf = self;
- __weak typeof(zoomableView) weakZoomableView = zoomableView;
- __weak typeof(indicator) weakIndicator = indicator;
-
- // 使用SDWebImage加载图片
- [zoomableView.imageView sd_setImageWithURL:imageURL
- placeholderImage:nil
- options:self.imageDownloadOptions
- progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
- // 加载进度回调
- dispatch_async(dispatch_get_main_queue(), ^{
- // if (expectedSize > 0) {
- // CGFloat progress = (CGFloat)receivedSize / (CGFloat)expectedSize;
- // weakIndicator.progress = progress;
- // }
- });
- } completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [weakIndicator stopAnimating];
- [weakIndicator removeFromSuperview];
-
- if (error) {
- [weakSelf showErrorAlert:@"图片加载失败"];
- [weakSelf dismissPreviewController];
- } else {
- [weakZoomableView setImage:image];
- [weakZoomableView setNeedsLayout];
- [weakZoomableView layoutIfNeeded];
- // 添加单击手势隐藏/显示控制按钮
- UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:weakSelf action:@selector(toggleControls:)];
- [previewController.view addGestureRecognizer:singleTap];
- self->_showimge =image;
- }
- });
- }];
- }
- #pragma mark - Private Methods - Video Preview
- - (void)previewVideoAtURL:(NSURL *)videoURL {
- NSLog(@"previewVideoAtURL: %@", videoURL);
- AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init];
- AVPlayer *player = [AVPlayer playerWithURL:videoURL];
- playerViewController.player = player;
- playerViewController.showsPlaybackControls = YES;
- [self presentViewController:playerViewController];
- // 尝试切换到耳机输出(如果可用)或扬声器(默认)
- NSError *error = nil;
-
-
- AVAudioSession *session = [AVAudioSession sharedInstance];
- // 设置音频会话的类别为播放
- [session setCategory:AVAudioSessionCategoryPlayback error:&error];
- if (error) {
- NSLog(@"设置音频会话类别失败: %@", error.localizedDescription);
- } else {
- // 尝试将输出端口覆盖为扬声器
- [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];// 或者AVAudioSessionPortOverrideSpeaker AVAudioSessionPortOverrideNone 保持默认行为
- if (error) {
- NSLog(@"设置输出端口为扬声器失败: %@", error.localizedDescription);
- } else {
- NSLog(@"已成功设置为扬声器播放模式");
- }
- }
- // 激活音频会话
- [session setActive:YES error:&error];
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [player play];
- });
-
-
- // __weak AVPlayerViewController *weakPlayerVC = playerViewController;
- // [playerViewController.player addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
- //
- // [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
- // object:playerViewController.player.currentItem
- // queue:[NSOperationQueue mainQueue]
- // usingBlock:^(NSNotification *note) {
- // [weakPlayerVC.player removeObserver:self forKeyPath:@"status"];
- // [[NSNotificationCenter defaultCenter] removeObserver:self
- // name:AVPlayerItemDidPlayToEndTimeNotification
- // object:weakPlayerVC.player.currentItem];
- // }];
-
- }
- #pragma mark - Private Methods - Document Preview
- - (void)previewDocumentAtURL:(NSURL *)documentURL {
- self.documentController = [UIDocumentInteractionController interactionControllerWithURL:documentURL];
- self.documentController.delegate = self;
-
- if (![self.documentController presentPreviewAnimated:YES]) {
- [self previewWithWebView:documentURL];
- }
- }
- #pragma mark - Private Methods - WebView Preview
- - (void)previewWithWebView:(NSURL *)url {
- UIViewController *webViewController = [[UIViewController alloc] init];
- WKWebView *webView = [[WKWebView alloc] initWithFrame:webViewController.view.bounds];
- webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- [webViewController.view addSubview:webView];
-
- [self addCloseButtonToController:webViewController];
- [self addLoadingIndicatorToView:webViewController.view];
-
- NSURLRequest *request = [NSURLRequest requestWithURL:url];
- [webView loadRequest:request];
-
- [self presentViewController:webViewController];
- self.webPreviewController = webViewController;
- }
- #pragma mark - UI Helpers
- - (void)addCloseButtonToController:(UIViewController *)controller {
- UIButton *closeButton = [UIButton buttonWithType:UIButtonTypeSystem];
- [closeButton setTitle:@"关闭" forState:UIControlStateNormal];
- [closeButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
- closeButton.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
- closeButton.frame = CGRectMake(20, 40, 60, 30);
- closeButton.layer.cornerRadius = 5;
- [closeButton addTarget:self action:@selector(dismissPreviewController) forControlEvents:UIControlEventTouchUpInside];
- [controller.view addSubview:closeButton];
- }
- - (void)showLoadingIndicator {
- if (!self.loadingIndicator) {
- self.loadingIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
- self.loadingIndicator.center = self.currentParentViewController.view.center;
- [self.currentParentViewController.view addSubview:self.loadingIndicator];
- }
- [self.loadingIndicator startAnimating];
- }
- - (void)hideLoadingIndicator {
- [self.loadingIndicator stopAnimating];
- [self.loadingIndicator removeFromSuperview];
- self.loadingIndicator = nil;
- }
- - (void)addLoadingIndicatorToView:(UIView *)view {
- UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
- indicator.center = view.center;
- [indicator startAnimating];
- [view addSubview:indicator];
- }
- - (void)showErrorAlert:(NSString *)message {
- [self hideLoadingIndicator];
-
- UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"错误"
- message:message
- preferredStyle:UIAlertControllerStyleAlert];
- [alert addAction:[UIAlertAction actionWithTitle:@"确定"
- style:UIAlertActionStyleDefault
- handler:nil]];
-
- [self.currentParentViewController presentViewController:alert animated:YES completion:nil];
- }
- - (void)presentViewController:(UIViewController *)viewController {
- viewController.modalPresentationStyle = UIModalPresentationFullScreen;
- [self.currentParentViewController presentViewController:viewController animated:YES completion:nil];
- }
- - (void)dismissPreviewController {
- [self.webPreviewController dismissViewControllerAnimated:YES completion:nil];
- self.webPreviewController = nil;
- [self hideLoadingIndicator];
- }
- - (void)saveImg {
- if(_showimge){
- UIImageWriteToSavedPhotosAlbum(_showimge, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
- }
- }
- - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
- if (error) {
- // 保存失败,输出错误信息
- NSLog(@"Error saving image: %@", error);
- } else {
- // 保存成功
- NSLog(@"Image saved successfully!");
- [MBProgressHUD showWithText:@"图片已保存到相册"];
- }
- }
- #pragma mark - Gesture Handlers
- - (void)handleDoubleTap:(UITapGestureRecognizer *)recognizer {
- UIImageView *imageView = (UIImageView *)recognizer.view;
-
- if (imageView.contentMode == UIViewContentModeScaleAspectFit) {
- [UIView animateWithDuration:0.3 animations:^{
- imageView.contentMode = UIViewContentModeScaleAspectFill;
- imageView.transform = CGAffineTransformMakeScale(2.0, 2.0);
- }];
- } else {
- [UIView animateWithDuration:0.3 animations:^{
- imageView.contentMode = UIViewContentModeScaleAspectFit;
- imageView.transform = CGAffineTransformIdentity;
- }];
- }
- }
- // 切换控制按钮显示/隐藏
- - (void)toggleControls:(UITapGestureRecognizer *)gesture {
- UIViewController *previewController = (UIViewController *)gesture.view.nextResponder;
- UIButton *closeButton = [previewController.view viewWithTag:100];
-
- UIButton *saveButton = [previewController.view viewWithTag:101];
-
- [UIView animateWithDuration:0.3 animations:^{
- closeButton.alpha = (closeButton.alpha == 0) ? 1 : 0;
- saveButton.alpha = (saveButton.alpha == 0) ? 1 : 0;
- }];
-
- }
- #pragma mark - File Type Detection
- - (BOOL)isImageFile:(NSString *)fileExtension {
- NSArray *imageExtensions = @[@"jpg", @"jpeg", @"png", @"gif", @"bmp", @"tiff", @"webp"];
- return [imageExtensions containsObject:fileExtension];
- }
- - (BOOL)isVideoFile:(NSString *)fileExtension {
- NSArray *videoExtensions = @[@"mp4", @"mov", @"m4v", @"avi", @"mkv", @"flv", @"wmv"];
- return [videoExtensions containsObject:fileExtension];
- }
- #pragma mark - UIDocumentInteractionControllerDelegate
- - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller {
- return self.currentParentViewController;
- }
- - (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller {
- self.documentController = nil;
- }
- @end
|