What you probably want to do...
- constrain all 4 sides of the stack view to the scroll view's Content Layout Guide
- constrain the Height of the stack view equal to the Height of the scroll view's Frame Layout Guide
- do NOT constrain the Width of the stack view
- set the stack view's Distribution to Fill
Create a "tab view" - here's an example with a 50 x 50 centered image view, rounded top corners and a 1-pt outline:

We can create that with this simple class:
class MyTabView: UIView {
    
    let imgView = UIImageView()
    
    init(with image: UIImage) {
        super.init(frame: .zero)
        imgView.image = image
        commonInit()
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        imgView.translatesAutoresizingMaskIntoConstraints = false
        // light gray background
        backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        addSubview(imgView)
        NSLayoutConstraint.activate([
            // centered
            imgView.centerXAnchor.constraint(equalTo: centerXAnchor),
            imgView.centerYAnchor.constraint(equalTo: centerYAnchor),
            // 50x50
            imgView.widthAnchor.constraint(equalToConstant: 50.0),
            imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor),
        ])
        
        // a little "styling" for the "tab"
        clipsToBounds = true
        layer.cornerRadius = 12
        layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
        layer.borderWidth = 1
        layer.borderColor = UIColor.darkGray.cgColor
    }
}
For each "tab" that we add to the stack view, we'll set its Width constraint equal to the scroll view's Frame Layout Guide widthAnchor with multiplier: 1.0 / 3.0. That way each "tab view" will be 1/3rd the width of the scroll view:



With 1, 2 or 3 "tabs" there will be no horizontal scrolling, because they all fit within the frame.
Once we have more than 3 "tabs" the stack view's width will exceed the width of the frame, and we'll have horizontal scrolling:

Here's the view controller I used for that. It creates 9 "tab images"... starts with a single "tab"... each tap will ADD a "tab" until we have all 9, at which point each tap will REMOVE a "tab":
class StackAsTabsViewController: UIViewController {
    
    let stackView: UIStackView = {
        let v = UIStackView()
        v.axis = .horizontal
        v.distribution = .fill
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    
    let scrollView: UIScrollView = {
        let v = UIScrollView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    // a label to show what's going on
    let statusLabel: UILabel = {
        let v = UILabel()
        v.numberOfLines = 0
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    
    // array to hold our "tab" images
    var images: [UIImage] = []
    // we'll add a "tab" on each tap
    //  until we reach the end of the images array
    //  then we'll remove a "tab" on each tap
    //  until we're back to a single "tab"
    var isAdding: Bool = true
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // add the "status" label
        view.addSubview(statusLabel)
        
        // add stackView to scrollView
        scrollView.addSubview(stackView)
        
        // add scrollView to view
        view.addSubview(scrollView)
        
        // respect safe area
        let g = view.safeAreaLayoutGuide
        
        // scrollView Content and Frame Layout Guides
        let contentG = scrollView.contentLayoutGuide
        let frameG = scrollView.frameLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // constrain scrollView Top / Leading / Trailing
            scrollView.topAnchor.constraint(equalTo: g.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            
            // height = 58 (image will be 50x50, so a little top and bottom padding)
            scrollView.heightAnchor.constraint(equalToConstant: 58.0),
            
            // constrain stackView all 4 sides to scrollView Content Layout Guide
            stackView.topAnchor.constraint(equalTo: contentG.topAnchor),
            stackView.leadingAnchor.constraint(equalTo: contentG.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: contentG.trailingAnchor),
            stackView.bottomAnchor.constraint(equalTo: contentG.bottomAnchor),
            
            // stackView Height equal to scrollView Frame Height
            stackView.heightAnchor.constraint(equalTo: frameG.heightAnchor),
            
            // statusLabel in the middle of the view
            statusLabel.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 40.0),
            statusLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            statusLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0)
            
        ])
        
        // let's create 9 images using SF Symbols
        for i in 1...9 {
            guard let img = UIImage(systemName: "\(i).circle.fill") else {
                fatalError("Could not create images!!!")
            }
            images.append(img)
        }
        
        // add the first "tab view"
        self.updateTabs()
        
        // tap anywhere in the view
        let t = UITapGestureRecognizer(target: self, action: #selector(gotTap(_:)))
        view.addGestureRecognizer(t)
    }
    
    @objc func gotTap(_ g: UITapGestureRecognizer) -> Void {
        updateTabs()
    }
    
    func updateTabs() -> Void {
        
        if isAdding {
            // get the next image from the array
            let img = images[stackView.arrangedSubviews.count]
            
            // create a "tab view"
            let tab = MyTabView(with: img)
            // add it to the stackView
            stackView.addArrangedSubview(tab)
            let frameG = scrollView.frameLayoutGuide
            NSLayoutConstraint.activate([
                // each "tab view" is 1/3rd the width of the scroll view frame
                tab.widthAnchor.constraint(equalTo: frameG.widthAnchor, multiplier: 1.0 / 3.0),
                // each "tab view" is the same height as the scroll view frame
                tab.heightAnchor.constraint(equalTo: frameG.heightAnchor),
            ])
        } else {
            stackView.arrangedSubviews.last?.removeFromSuperview()
        }
        if stackView.arrangedSubviews.count == 1 {
            isAdding = true
        } else if stackView.arrangedSubviews.count == images.count {
            isAdding = false
        }
        
        updateStatusLabel()
        
    }
    
    func updateStatusLabel() -> Void {
        
        // we'll do this async, to make sure the views have been updated
        DispatchQueue.main.async {
            let numTabs = self.stackView.arrangedSubviews.count
            var str = ""
            if self.isAdding {
                str += "Tap anywhere to ADD a tab"
            } else {
                str += "Tap anywhere to REMOVE a tab"
            }
            str += "\n\n"
            str += "Number of tabs: \(numTabs)"
            str += "\n\n"
            if numTabs > 3 {
                str += "Tabs WILL scroll"
            } else {
                str += "Tabs will NOT scroll"
            }
            self.statusLabel.text = str
        }
        
    }
}
Play with it, and see if that's what you're going for.