Skip to content

Instantly share code, notes, and snippets.

@bergusman
Last active March 20, 2024 17:41
Show Gist options
  • Save bergusman/8d511ebfd67ea3f8498f62261d316c93 to your computer and use it in GitHub Desktop.
Save bergusman/8d511ebfd67ea3f8498f62261d316c93 to your computer and use it in GitHub Desktop.
Handle camera permission for web image file input
//
// WebWithCameraViewController.swift
//
// Created by Vitaly Berg on 3/20/24.
//
import UIKit
import WebKit
import AVFoundation
let html = """
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body style="padding: 20px">
<input type="file" accept="image/*" />
</body>
</html>
"""
class WebWithCameraViewController: UIViewController {
private var webView: WKWebView!
// MARK: - UIViewController
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView()
webView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(webView)
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.topAnchor),
webView.leftAnchor.constraint(equalTo: view.leftAnchor),
webView.rightAnchor.constraint(equalTo: view.rightAnchor),
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
webView.loadHTMLString(html, baseURL: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let imagePickerVC = presentedViewController as? UIImagePickerController {
if imagePickerVC.sourceType != .camera {
// Optional checking, system not use it to pick photos, instead it uses PHPickerViewController
return
}
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch status {
case .notDetermined:
// Request permissions by UIImagePickerController
break
case .authorized:
// All good!
break
case .restricted:
// Sometimes when restricted by parental controls
showRestricted(for: imagePickerVC)
case .denied:
// User denied
showDenied(for: imagePickerVC);
@unknown default:
break
}
}
}
private func showDenied(for imagePickerVC: UIImagePickerController) {
showPermissionsAlert(for: imagePickerVC, restricted: false)
}
private func showRestricted(for imagePickerVC: UIImagePickerController) {
showPermissionsAlert(for: imagePickerVC, restricted: true)
}
private var useCustomPermissionsView = false
private func showPermissionsAlert(for imagePickerVC: UIImagePickerController, restricted: Bool) {
// First option with modifing camera screen
if useCustomPermissionsView {
if let overlayView = imagePickerVC.cameraOverlayView {
// TODO: don't want to create good example
let customView = UIView(frame: overlayView.bounds)
customView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
customView.backgroundColor = .black
overlayView.addSubview(customView)
let label = UILabel(frame: customView.bounds)
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.font = .systemFont(ofSize: 100, weight: .bold)
label.text = ":("
label.textAlignment = .center
label.textColor = .white
customView.addSubview(label)
let button = UIButton(type: .system)
button.setTitle("Dismiss", for: .normal)
button.addAction(.init(handler: { [weak self] _ in
self?.dismissImagePicker(imagePickerVC)
}), for: .touchUpInside)
button.tintColor = .white
button.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(button)
button.centerXAnchor.constraint(equalTo: customView.centerXAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: customView.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = true
return
}
}
// Second option with alert and fallback in case of first option
let alertVC = UIAlertController(title: "No Permissions", message: "Something about that need enable permissions", preferredStyle: .alert)
if restricted {
// TODO: handle a little different than denied, for example change message and open settings app
alertVC.addAction(.init(title: "Open Settings", style: .default, handler: { [weak self] _ in
self?.dismissImagePicker(imagePickerVC)
}))
} else {
alertVC.addAction(.init(title: "Open App Settings", style: .default, handler: { [weak self] _ in
self?.dismissImagePicker(imagePickerVC)
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
}))
}
alertVC.addAction(.init(title: "Ok", style: .cancel, handler: { [weak self] _ in
self?.dismissImagePicker(imagePickerVC)
}))
imagePickerVC.present(alertVC, animated: true)
}
// It just dismisses picker. Context menu on input will be unaviable if just dismiss without delegate invoking.
private func dismissImagePicker(_ imagePickerVC: UIImagePickerController) {
if imagePickerVC.delegate?.imagePickerControllerDidCancel != nil {
imagePickerVC.delegate?.imagePickerControllerDidCancel?(imagePickerVC)
} else {
// Fallback
imagePickerVC.dismiss(animated: true)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment