How to ignore the safeArea while in fullscreen?

104 views Asked by At

I want to implement an image preview feature, but I encountered a problem: when the image is in full screen, even though the status bar is hidden, the safe area remains blank, as shown in the picture. img1 How to successfully ignore the safe area?

import SwiftUI
import UIKit

class PreviewViewController : UIViewController {
    var isDarkMode: Bool = false {
            didSet {
                updateBackgroundColor()
            }
        }
    
    var image: UIImage? {
        didSet {
            guard let image = image else {
                imageView.image = nil
                return
            }
            imageView.image = image
        }
    }
    let imageView: UIImageView = UIImageView(frame: .zero)
    
    var fullscreen = false {
        didSet {
            guard oldValue != fullscreen else { return }
            UIView.animate(withDuration: 0.3) {
                self.updateNavigationBar()
                self.updateStatusBar()
                self.updateBackgroundColor()
            }
        }
    }

    let scrollView = UIScrollView(frame: .zero)
    let singleTapRecognizer = UITapGestureRecognizer()
    let doubleTapRecognizer = UITapGestureRecognizer()

    override var prefersStatusBarHidden : Bool {
        return fullscreen
    }

    required init() {
        super.init(nibName: nil, bundle: nil)
        setupScrollView()
        setupImageView()
        setupSingleTapRecognizer()
        setupDoubleTapRecognizer()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupScrollView() {
        scrollView.frame = view.bounds
        scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        scrollView.delegate = self
        scrollView.minimumZoomScale = 1
        scrollView.maximumZoomScale = 3
        if #available(iOS 11.0, *) {
            scrollView.contentInsetAdjustmentBehavior = .never
        }
        scrollView.showsVerticalScrollIndicator = false
        scrollView.showsHorizontalScrollIndicator = false
        
        view.addSubview(scrollView)
    }

    private func setupImageView() {
        imageView.frame = scrollView.bounds
        imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        imageView.contentMode = .scaleAspectFit
        scrollView.addSubview(imageView)
    }

    private func setupSingleTapRecognizer() {
        singleTapRecognizer.numberOfTapsRequired = 1
        singleTapRecognizer.addTarget(self, action: #selector(didSingleTap(_:)))
        singleTapRecognizer.require(toFail: doubleTapRecognizer)
        view.addGestureRecognizer(singleTapRecognizer)
    }

    private func setupDoubleTapRecognizer() {
        doubleTapRecognizer.numberOfTapsRequired = 2
        doubleTapRecognizer.addTarget(self, action: #selector(didDoubleTap(_:)))
        view.addGestureRecognizer(doubleTapRecognizer)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        updateBackgroundColor()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        fullscreen = false
    }
    
    private func toggleFullscreen() {
        fullscreen = !fullscreen
    }

    @objc func didSingleTap(_ recognizer: UIGestureRecognizer) {
        toggleFullscreen()
    }

    @objc func didDoubleTap(_ recognizer: UIGestureRecognizer) {
        if scrollView.zoomScale > 1 {
            scrollView.setZoomScale(1, animated: true)
        } else {
            scrollView.zoom(to: zoomRect(scale: 2, center: recognizer.location(in: recognizer.view)), animated: true)
        }
    }

    private func zoomRect(scale: CGFloat, center: CGPoint) -> CGRect {
        guard let zoomView = viewForZooming(in: scrollView) else { return .zero }
        let newCenter = scrollView.convert(center, from: zoomView)

        var zoomRect = CGRect.zero
        zoomRect.size.height = zoomView.frame.size.height / scale
        zoomRect.size.width = zoomView.frame.size.width / scale
        zoomRect.origin.x = newCenter.x - (zoomRect.size.width / 2.0)
        zoomRect.origin.y = newCenter.y - (zoomRect.size.height / 2.0)

        return zoomRect
    }
    
    private func updateNavigationBar() {
        navigationController?.setNavigationBarHidden(fullscreen, animated: true)
    }
    
    private func updateStatusBar() {
        self.setNeedsStatusBarAppearanceUpdate()
    }
    
    private func updateBackgroundColor() {
        let aColor: UIColor
        
        if self.fullscreen {
            aColor = UIColor.black
        } else {
            print("Dark Mode Status: \(isDarkMode)")
            aColor = isDarkMode ? UIColor.black : UIColor.white
        }
        
        view.backgroundColor = aColor
    }
}

extension PreviewViewController: UIScrollViewDelegate {
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        if scrollView.zoomScale > 1 {
            fullscreen = true

            guard let image = imageView.image else { return }
            guard let zoomView = viewForZooming(in: scrollView) else { return }

            let widthRatio = zoomView.frame.width / image.size.width
            let heightRatio = zoomView.frame.height / image.size.height

            let ratio = widthRatio < heightRatio ? widthRatio:heightRatio

            let newWidth = image.size.width * ratio
            let newHeight = image.size.height * ratio

            let left = 0.5 * (newWidth * scrollView.zoomScale > zoomView.frame.width ? (newWidth - zoomView.frame.width) : (scrollView.frame.width - scrollView.contentSize.width))
            let top = 0.5 * (newHeight * scrollView.zoomScale > zoomView.frame.height ? (newHeight - zoomView.frame.height) : (scrollView.frame.height - scrollView.contentSize.height))

            scrollView.contentInset = UIEdgeInsets(top: top.rounded(), left: left.rounded(), bottom: top.rounded(), right: left.rounded())
        } else {
            scrollView.contentInset = .zero
        }
    }
}

struct PreviewView: UIViewControllerRepresentable {
    var darkMode: Bool
    var image: UIImage?
    
    func makeUIViewController(context: Context) -> PreviewViewController {
        let viewController = PreviewViewController()
        viewController.isDarkMode = darkMode
        viewController.image = image
        return viewController
    }

    func updateUIViewController(_ uiViewController: PreviewViewController, context: Context) {
        uiViewController.isDarkMode = darkMode
        uiViewController.image = image
    }
}

struct ImagePreview_Previews: PreviewProvider {
    static var previews: some View {
        PreviewView(darkMode: false, image: UIImage(named: "ppsd"))
    }
}

I tried using AutoLayout, but the blank space still exists.

view.addSubview(scrollView)
        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])

I also tried to solve the problem through ChatGPT-4, but without exception.

0

There are 0 answers