I have a child-view controller that do not want to have system safe areas or viewSafeAreaInsetsDidChange()
called on rotation. So far this is not working:
In Superview:
let childVC = UIViewController()
self.addChild(childVC)
childVC.view.frame = CGRect(x:0, y: self.view.bounds.height - 300, width: self.view.bounds.width, height: 300)
self.view.addSubview(childVC.view)
childVC.didMove(toParent: self)
In Child View Controller:
class childVC: UIViewController {
let picker = UIPickerView()
override final func loadView() {
super.loadView()
self.viewRespectsSystemMinimumLayoutMargins = false
self.view.insetsLayoutMarginsFromSafeArea = false
self.view.preservesSuperviewLayoutMargins. = false
}
override final func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//Some mention this to be inside viewDidAppear
self.viewRespectsSystemMinimumLayoutMargins = false
self.view.insetsLayoutMarginsFromSafeArea = false
self.view.preservesSuperviewLayoutMargins = false
//Add picker view here
self.picker.delegate = self
self.picker.dataSource = self
self.picker.frame = self.view.bounds
self.view.addSubview(self.picker)
}
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
//This gets called everytime the device rotates, causing the picker view to redraw and reload all components. Trying to avoid this method being called.
print ("viewSafeAreaInsetsDidChange")
print (self.view.safeAreaInsets)
}
}
Everytime the device rotates, viewSafeAreaInsetsDidChange()
gets called causing the picker view to redraw and reload.
Trying to avoid viewSafeAreaInsetsDidChange()
being called every time the device rotates.
viewSafeAreaInsetsDidChange()
Called to notify the view controller that the safe area insets of its root view changed.
This is a notification call. You ignore it, or respond to it... but you cannot prevent the safe area insets from changing.
These two examples: https://pastebin.com/vLkNj6vy and https://pastebin.com/cZPTZ17C show that viewSafeAreaInsetsDidChange()
is called on device rotation, but the picker view does NOT reload.
The picker view will reload if its frame is outside the affected safe-area change.
Here is a quick example:
class MovingPickerVC: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
let picker = UIPickerView()
var vConstraints: [NSLayoutConstraint] = []
var hConstraints: [NSLayoutConstraint] = []
override func viewDidLoad() {
super.viewDidLoad()
picker.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(picker)
// keep picker INSIDE the safe-area
// this WILL NOT cause reload on device rotation
let g = view.safeAreaLayoutGuide
// extend picker frame OUTSIDE the safe-area
// this WILL cause reload on device rotation
//let g = self.view!
NSLayoutConstraint.activate([
picker.widthAnchor.constraint(equalToConstant: 300.0),
picker.heightAnchor.constraint(equalToConstant: 160.0),
])
vConstraints = [
picker.centerXAnchor.constraint(equalTo: g.centerXAnchor),
picker.bottomAnchor.constraint(equalTo: g.bottomAnchor),
]
hConstraints = [
picker.centerYAnchor.constraint(equalTo: g.centerYAnchor),
picker.trailingAnchor.constraint(equalTo: g.trailingAnchor),
]
// so we can see its frame
picker.backgroundColor = .yellow
self.picker.delegate = self
self.picker.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if self.traitCollection.verticalSizeClass == .regular {
NSLayoutConstraint.activate(vConstraints)
} else {
NSLayoutConstraint.activate(hConstraints)
}
}
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: newCollection, with: coordinator)
coordinator.animate(alongsideTransition: { [unowned self] _ in
if newCollection.verticalSizeClass == .regular {
NSLayoutConstraint.deactivate(self.hConstraints)
NSLayoutConstraint.activate(self.vConstraints)
} else {
NSLayoutConstraint.deactivate(self.vConstraints)
NSLayoutConstraint.activate(self.hConstraints)
}
}) { [unowned self] _ in
// if we want to do something on completion
}
}
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
//This gets called everytime the device rotates, causing the picker view to redraw and reload all components. Trying to avoid this method being called.
print ("viewSafeAreaInsetsDidChange")
print (self.view.safeAreaInsets)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
print("num components: 1")
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
print("num rows: 30")
return 30
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
print("Title for Row:", row)
return "Row: \(row)"
}
}
Looks like this:
and rotating the device:
When that is run, we will see the viewSafeAreaInsetsDidChange()
being logged, but the picker view will NOT reload.
However, in viewDidLoad()
, if we change the constraints to the view instead of the safe area:
// keep picker INSIDE the safe-area
// this WILL NOT cause reload on device rotation
//let g = view.safeAreaLayoutGuide
// extend picker frame OUTSIDE the safe-area
// this WILL cause reload on device rotation
let g = self.view!
we've placed the picker view frame outside the safe area, and it will reload on rotation.
If you don't want the picker to reload (for some reason), you'll need to manage that via your dataSource / delegate funcs.