0

Consider that a view is added as subview to the main view as follows:

override func viewDidLoad()
{
    super.viewDidLoad()
    let subview = UIView(frame: CGRect(x: 150, y: 350, width: 20, height: 20))
    subview.backgroundColor = UIColor.blue
    self.view.addSubview(subview)
}

When the device goes from portrait to landscape, the default behaviour is that the subview is moved in a way that its horizontal and vertical distance from the upper left corner of the device, remain unchanged. How can i change this so that its horizontal and vertical distance from the center of the device, remain unchanged? Thank you

ckc
  • 345
  • 4
  • 12

2 Answers2

1

The easiest way is to use constraints instead of a fixed frame for your view. The code would look something like this:

override func viewDidLoad()
{
    super.viewDidLoad()
    let subview = UIView()

    /// You do not need to refer to self and UIColor, Swift does that for you.
    subview.backgroundColor = .blue
    view.addSubview(subview)

    /// Do not forget the following line
    subview.translatesAutoresizingMaskIntoConstraints = false

    /// Create and activate your constraints in one step.
    NSLayoutConstraint.activate([
        /// Set the height and width of your subview
        subview.heightAnchor.constraint(equalToConstant: 20),
        subview.widthAnchor.constraint(equalToConstant: 20),

        /// This centers the subview vertically and horizontally in the parent view
        subview.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        subview.centerYAnchor.constraint(equalTo: view.centerYAnchor)
    ])
}

Also you should refactor the code above, e.g. create method that sets up everything and call it inside of viewDidLoad.

laka
  • 644
  • 8
  • 23
  • You are very welcome, you can also set it as the accepted answer so that other people can find it faster. – laka Apr 16 '20 at 15:00
  • Dear Iaka, I cannot accept it as the correct answer, since your approach just puts the subview in the center of the superview both in portrait and in landscape. My question was the following: when a subview is in an arbitrary place in the superview (either in its center or not), if the orientation is changed the system sets the new layout by keeping as constant the subview's horizontal and vertical distance from the upper left corner of the superview. I would like the new layout to be set by keeping as constant the horizontal and vertical distance from the center of the superview. – ckc Apr 17 '20 at 19:35
  • 1
    Ok, sorry. Then I do not really understand what you are trying to achieve. If you do not want to center both views on each other you can pass in a constant. I would recommend using AutoLayout whenever working with UIViews. This is way more robust than doing pixel calculations on your own. – laka Apr 19 '20 at 21:18
  • The target is that the distance of the two views' centers will remain the same before and after the orientation of the device. Note that I will add gestures that may change the subview's center (the superview's center will remain unchanged). Can this be implemented with AutoLayout? – ckc Apr 21 '20 at 08:39
  • 1
    You can save constraints to a let and set its constant property to "shift" it around. For example: `let constraint = subView.centerXAnchor.constraint(equalTo: superView.centerXAnchor)` Then you can offset your constraint like that: `constraint.constant = 123`. Do not forget to activate your constraint before (like shown in my example) or just by calling `constraint.isActive = true` – laka Apr 23 '20 at 13:17
  • Ok, I will try your approach. Thanks again – ckc Apr 24 '20 at 18:53
0

I have found the answer:

 override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)
{
    super.willTransition(to: newCollection, with: coordinator)
    //here, the distance (just before the orientation) between the subview's center and superview's center can be derived
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
{
    super.traitCollectionDidChange(previousTraitCollection)
    //here, the distance derived above can be used to set the subview to the desired position in it's superview
}

I have described how these two functions work in an asnwer to this post: Swift - How to detect orientation changes

ckc
  • 345
  • 4
  • 12