1

I programmatically created a stackView with a UISegmentedControl and a Map with a MKMapView. I am using CGRect to size and place the frame of the stackView in the interface but when I switch the device simulator the stackView moves since it does not have any constraints. I usually use the StoryBoard to add constraints to anything but have never programmatically done it my self. I have seen other examples here in Stack Overflow but so far It doesn't help with what it is that I am trying to do. Here is my code showing how I am displaying it in the viewDidLoad(). If someone could explain how to add constraints programmatically so that the stackView covers 1/4 of the screen Y axis:547 please and thank you.

import UIKit
import MapKit

class SampleViewController: UIViewController {


override func viewDidLoad() {
    super.viewDidLoad()
    
    let paddedStackView = UIStackView(arrangedSubviews: [segmentedControl])
    paddedStackView.layoutMargins = .init(top: 12, left: 12, bottom: 6, right: 12)
    paddedStackView.isLayoutMarginsRelativeArrangement = true
    
    let stackView = UIStackView(arrangedSubviews: [
        paddedStackView, map
        ])
    stackView.axis = .vertical
    view.addSubview(stackView)
    
    stackView.frame =  CGRect(x: 0, y: 547, width: 390, height: 350)

}

let map = MKMapView()

let segmentedControl:UISegmentedControl = {
    let sc = UISegmentedControl(items: ["Arriving", "Leaving"])
    sc.selectedSegmentIndex = 0
    sc.addTarget(self, action: #selector(handleSegmentControl), for: .valueChanged)
    return sc
}()

  @objc func handleSegmentControl(){
    
    switch segmentedControl.selectedSegmentIndex {
    case 0:
        print("Entered Destination")
    case 1:
        print("Exited Destination")
    default:
        print("Error")
    }

}
General Grievance
  • 4,555
  • 31
  • 31
  • 45
Chamoy
  • 41
  • 7

2 Answers2

2

If your question is how to convert this:

stackView.frame =  CGRect(x: 0, y: 547, width: 390, height: 350)

and make the height 1/4 of the screen instead of 350

Converting this request into auto layout constraints would go something like this

private func configureStackView()
{
    let paddedStackView = UIStackView(arrangedSubviews: [segmentedControl])
    paddedStackView.layoutMargins = .init(top: 12,
                                          left: 12,
                                          bottom: 6,
                                          right: 12)
    
    paddedStackView.isLayoutMarginsRelativeArrangement = true
    
    let stackView = UIStackView(arrangedSubviews: [paddedStackView, map])
    stackView.axis = .vertical
    view.addSubview(stackView)
    
    // AUTO LAYOUT
    
    // This is important for using auto layout
    // Setting this to false means the frame is ignored
    // So sizing and positioning should be set by NSLayoutConstraints
    stackView.translatesAutoresizingMaskIntoConstraints = false
    
    // Add the view to its parent before adding any constraints
    view.addSubview(stackView)
    
    // Add constraints equivalent to:
    // CGRect(x: 0, y: 547, width: 390, height: 350)
    view.addConstraints([
        // this is x origin
        stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        
        // this is y origin
        stackView.topAnchor.constraint(equalTo: view.topAnchor,
                                       constant: 547),
        
        // this is the width
        stackView.widthAnchor.constraint(equalToConstant: 390),
        
        // this is height, thanks to @flanker for this update
        stackView.heightAnchor.constraint(equalTo: view.heightAnchor,
                                          multiplier: 0.75)
    ])
}

This should give you what you are looking for.

With that being said, you probably want to check out this SO thread as there are different ways to add auto layout constraints programmatically so see which way you prefer

Update with screenshot

The above configuration gives something like this

Programmatically create UIStackView with auto layout constraints swift iOS

Shawn Frank
  • 4,381
  • 2
  • 19
  • 29
  • 1
    While generally ok, using a constant for the height could cause problems if screen orientation or size (think iPad) changes. I'd prefer calculating it in the constraint, probably using the parent views constraint and a multiplier 0f 0.75, so it's always correct when constraints are reapplied. – flanker Feb 28 '22 at 14:15
  • @flanker - thanks for that tip and I agree with you, that is a better way to do things than my initial solution. I have updated the answer with this - thanks again ! – Shawn Frank Feb 28 '22 at 14:26
  • @ShawnFrank Everything works well except the map view does not show only the segmented control when setting the translatesAutoresizingMaskIntoConstraints to false. – Chamoy Feb 28 '22 at 17:20
  • @Chamoy - there was a small bug with the height anchor, I have updated it with a screenshot, please use the latest code above. – Shawn Frank Mar 01 '22 at 03:27
  • @ShawnFrank It works perfectly fine now and thank you for the comments in your code, helped me understand it well. – Chamoy Mar 01 '22 at 03:46
0
    //MARK: UIStackView
    
    let stackview = UIStackView()
    stackview.axis = .vertical
    stackview.spacing = 2
    stackview.distribution = .fill
    
    //MARK: Stackview Elements
    let view_left = UIView()
    let view_right = UIView()
    
    //MARK: initial added View
    stackview.addArrangedSubview(view_left)
    stackview.addArrangedSubview(view_right)
    
    //MARK: remove Run time stack elements
    
    for view in stackview.subviews {
        view.removeFromSuperview()
    }
    
   //MARK: Again assign values Run time
    
    stackview.insertArrangedSubview(view_right, at: 0)
    stackview.insertArrangedSubview(view_left, at: 1)
koen
  • 5,383
  • 7
  • 50
  • 89
Akila M
  • 42
  • 7