You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改WKWebView的Action Sheet按钮?解决保存图片崩溃问题

解决WKWebView长按保存图片时无权限崩溃的问题

这个坑我之前做WKWebView项目时正好踩过!默认的「Save image」动作完全不做权限校验,没开照片库权限直接点击就会崩溃。下面是一套实测有效的完整解决方案,从权限拦截到保存逻辑都给你梳理清楚了:

1. 导入必要框架

首先在你的ViewController文件顶部导入Photos框架,毕竟要和照片库交互:

import Photos

2. 拦截默认保存动作,替换为自定义逻辑

给WKWebView设置UIDelegate,然后实现performActionFor方法——这个方法会在用户触发WebView内的交互动作(比如长按图片的Save image)时被调用。我们在这里拦截图片保存请求,自己处理权限检查和保存流程:

extension YourViewController: WKUIDelegate {
    func webView(_ webView: WKWebView, performActionFor hitTestResult: WKHitTestResult, completionHandler: @escaping () -> Void) {
        // 判断当前动作是否为保存图片
        guard hitTestResult.type == .image, let imageURL = hitTestResult.imageURL else {
            // 非图片保存动作,执行默认逻辑
            completionHandler()
            return
        }

        // 先检查照片库权限
        checkPhotoPermission { [weak self] hasPermission in
            guard let self = self else { return }
            if hasPermission {
                // 有权限,直接下载并保存图片
                self.saveImage(from: imageURL, completion: completionHandler)
            } else {
                // 无权限,发起权限申请
                PHPhotoLibrary.requestAuthorization { status in
                    DispatchQueue.main.async {
                        switch status {
                        case .authorized, .limited:
                            self.saveImage(from: imageURL, completion: completionHandler)
                        case .denied, .restricted:
                            // 权限被拒绝,提示用户前往设置开启
                            let alert = UIAlertController(title: "需要照片库权限", message: "请在设置中开启权限才能保存图片", preferredStyle: .alert)
                            alert.addAction(UIAlertAction(title: "取消", style: .cancel))
                            alert.addAction(UIAlertAction(title: "去设置", style: .default) { _ in
                                guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return }
                                UIApplication.shared.open(settingsURL)
                            })
                            self.present(alert, animated: true)
                            completionHandler()
                        case .notDetermined:
                            // 理论上不会走到这里,因为前面已做权限检查
                            completionHandler()
                        @unknown default:
                            completionHandler()
                        }
                    }
                }
            }
        }
    }

    // 封装权限检查的辅助方法
    private func checkPhotoPermission(completion: @escaping (Bool) -> Void) {
        let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
        switch status {
        case .authorized, .limited:
            completion(true)
        case .denied, .restricted:
            completion(false)
        case .notDetermined:
            PHPhotoLibrary.requestAuthorization { status in
                DispatchQueue.main.async {
                    completion(status == .authorized || status == .limited)
                }
            }
        @unknown default:
            completion(false)
        }
    }

    // 下载图片并保存到照片库
    private func saveImage(from url: URL, completion: @escaping () -> Void) {
        URLSession.shared.dataTask(with: url) { data, _, error in
            DispatchQueue.main.async {
                defer { completion() }
                // 校验图片数据有效性
                guard let imageData = data, let image = UIImage(data: imageData) else {
                    let alert = UIAlertController(title: "保存失败", message: "无法获取图片数据", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "确定", style: .default))
                    self.present(alert, animated: true)
                    return
                }

                // 执行保存操作
                PHPhotoLibrary.shared().performChanges({
                    PHAssetChangeRequest.creationRequestForAsset(from: image)
                }) { success, error in
                    DispatchQueue.main.async {
                        if success {
                            let alert = UIAlertController(title: "保存成功", message: "图片已存入照片库", preferredStyle: .alert)
                            alert.addAction(UIAlertAction(title: "确定", style: .default))
                            self.present(alert, animated: true)
                        } else {
                            let errorMsg = error?.localizedDescription ?? "保存失败,请稍后重试"
                            let alert = UIAlertController(title: "保存失败", message: errorMsg, preferredStyle: .alert)
                            alert.addAction(UIAlertAction(title: "确定", style: .default))
                            self.present(alert, animated: true)
                        }
                    }
                }
            }
        }.resume()
    }
}

3. 绑定WKWebView的UIDelegate

别忘了在初始化WKWebView时,把它的uiDelegate设置为当前ViewController,否则上面的拦截方法不会生效:

let webView = WKWebView(frame: view.bounds)
webView.uiDelegate = self
view.addSubview(webView)

4. 配置Info.plist权限描述

最后必须在Info.plist中添加权限说明,否则iOS会直接拒绝权限请求:

<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问照片库以保存您喜欢的图片</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>需要访问照片库以保存您喜欢的图片</string>

额外说明

  • 兼容iOS 14+的limited权限(仅允许访问部分照片),覆盖更多场景
  • 所有UI操作都切回主线程,避免线程冲突问题
  • 保存成功/失败都添加了弹窗提示,提升用户体验

内容的提问来源于stack exchange,提问作者Babac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:41:20