SwiftUI view is adjusting itself near the safe area

Viewed 460

I'm having an issue while moving a view that contains a SwiftUI view near the edges of the screen. The SwiftUI view moves itself to avoid being blocked by the safe area insets

I'm using UIKit to handle dragging the view via UIHostingController and a UIPanGestureRecognizer. Here's the code

import UIKit

class ViewController: UIViewController {
    
    var contentView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        let contentVc = UIHostingController(rootView: Content())
        addChild(contentVc)
        contentView = contentVc.view
        let contentHeight = contentView.sizeThatFits(view.bounds.size).height
        contentView.frame = CGRect(x: 0, y: 200, width: view.bounds.width, height: contentHeight)
        view.addSubview(contentView)
        contentVc.didMove(toParent: self)
        
        let drag = UIPanGestureRecognizer(target: self, action: #selector(drag(_:)))
        contentView.addGestureRecognizer(drag)
        
        view.backgroundColor = .orange
    }
    
    var startingPoint = CGPoint.zero
    @objc func drag(_ gesture: UIPanGestureRecognizer) {
        switch gesture.state {
        case .began:
            startingPoint = contentView.frame.origin
        case .changed:
            let location = gesture.translation(in: view)
            contentView.frame.origin.y = startingPoint.y + location.y
        default:
            break
        }
    }
}


import SwiftUI

struct Content: View {
    var body: some View {
        VStack {
            ForEach(0..<10) { i in
                Text(String(i))
            }
        }
        .background(Color.blue)
        .padding(.vertical, 32)
        
        .ignoresSafeArea()
    }
}

What I'm expecting is the blue view to not to adjust itself vertically (in the preview the blue view's top padding is shrinking)

1 Answers

I found 2 ways for solving this issue:

Way1: Using UIKit Gesture!

class ViewController: UIViewController {
    
    var contentView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        let contentVc = UIHostingController(rootView: Content())
        addChild(contentVc)
        contentView = contentVc.view
        let contentHeight = contentView.sizeThatFits(view.bounds.size).height
        contentView.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: contentHeight)
        contentView.backgroundColor = .clear
        view.addSubview(contentView)
        contentVc.didMove(toParent: self)
        
        let drag = UIPanGestureRecognizer(target: self, action: #selector(drag(_:)))
        contentView.addGestureRecognizer(drag)
        
        view.backgroundColor = .orange
    }
    
    var startingPoint = CGPoint.zero
    @objc func drag(_ gesture: UIPanGestureRecognizer) {
        switch gesture.state {
        case .began:
            startingPoint = contentView.frame.origin
        case .changed:
            let location = gesture.translation(in: view)
            contentView.frame.origin.y = startingPoint.y + location.y
        default:
            break
        }
    }
}


import SwiftUI

struct Content: View {

    var body: some View {

        VStack {
            ForEach(0..<10) { i in
                Text(String(i))
            }
        }
        .padding()
        .background(Color.blue)
        .frame(maxWidth: .infinity)
        .background(Color.white)

    }
}

Way2: Using SwiftUI Gesture!

class ViewController: UIViewController {

    var contentView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        let contentVc = UIHostingController(rootView: Content())
        addChild(contentVc)
        contentView = contentVc.view
        contentView.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
        contentView.backgroundColor = .clear
        view.addSubview(contentView)
        contentVc.didMove(toParent: self)

        view.backgroundColor = .orange
    }

}


struct Content: View {

    @State private var offset: CGFloat = .zero
    @State private var lastOffset: CGFloat = .zero

    var body: some View {

        VStack {
            ForEach(0..<10) { i in
                Text(String(i))
            }
        }
        .padding()
        .background(Color.blue)
        .frame(maxWidth: .infinity)
        .background(Color.white)
        .offset(y: offset)
        .gesture(DragGesture(minimumDistance: .zero, coordinateSpace: .local).onChanged { value in

            offset = lastOffset + value.translation.height

        }
        .onEnded { value in
            lastOffset = lastOffset + value.translation.height
            offset = lastOffset
        })

    }
}
Related