swiftuiviewuiscrollviewconstraintsinputaccessoryview

How to implement a scrollView inside inputAccessoryView [Swift]


In my swift app I'm working with inputActivityView, really hard, and my idea is to add a scroll view to this view with 2 subviews and paging enable. Here's what I've done, I think the problem are constraints but I don't know how to solve it.

  lazy var scrollView: UIScrollView = {
        let sv = UIScrollView(frame: self.bounds)
        sv.backgroundColor = .blue
        sv.isPagingEnabled = true
        sv.contentSize = .init(width: 2 * self.frame.width, height: 54)
        return sv
    }()

    override init(frame: CGRect) { // the init of the customInputAccessoryView
        super.init(frame: frame)
        setup()
    }

    override var intrinsicContentSize: CGSize {
        return .zero
    }

    func setup() {
        backgroundColor = .red
        autoresizingMask = .flexibleHeight

        addSubview(scrollView)
        scrollView.fillSuperview()
        scrollView.heightAnchor.constraint(equalToConstant: 54).isActive = true


        firstView = UIView(frame: .init(origin: .zero, size: .init(width: frame.width, height: 54)))
        firstView.frame.origin = .zero
        firstView.backgroundColor = .gray
        firstView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(firstView)

        secondView = UIView(frame: firstView.bounds)
        secondView.frame.origin.x = frame.width
        secondView.backgroundColor = .lightGray
        secondView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(secondView)

        addConstraints()
    }

    private func addConstraints() {
        NSLayoutConstraint.activate([
            firstView.widthAnchor.constraint(equalToConstant: frame.width),
            firstView.heightAnchor.constraint(equalToConstant: 54)
       ])
    }

How can I set the constraints for the subviews, because in this way appear only the first view, and I can't scroll to the second one.


Solution

  • Yes, you're missing some constraints.

    First, no need to instantiate views with UIView(frame: ...) if you are then setting .translatesAutoresizingMaskIntoConstraints = false because the frame you just gave it will be ignored.

    Second, if you have your constraints setup correctly, no need to set a scroll view's .contentSize

    // don't do this
    //sv.contentSize = .init(width: 2 * self.frame.width, height: 54)
    

    Third, when configuring subviews of a scroll view, make sure your constraints define Top / Leading / Bottom / Trailing AND Width and Height.

    Here's an edited version of your code to try:

    class MyInputAccessoryView: UIView {
    
        lazy var scrollView: UIScrollView = {
            let sv = UIScrollView()
            sv.backgroundColor = .blue
            sv.isPagingEnabled = true
            // no need for this
            //sv.contentSize = .init(width: 2 * self.frame.width, height: 54)
            return sv
        }()
    
        var firstView: UIView!
        var secondView: UIView!
    
        override init(frame: CGRect) { // the init of the customInputAccessoryView
            super.init(frame: frame)
            setup()
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override var intrinsicContentSize: CGSize {
            return .zero
        }
    
        func setup() {
            backgroundColor = .red
            autoresizingMask = .flexibleHeight
    
            addSubview(scrollView)
            //scrollView.fillSuperview()
            scrollView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                scrollView.topAnchor.constraint(equalTo: topAnchor),
                scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
                scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
                scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
                scrollView.heightAnchor.constraint(equalToConstant: 54),
            ])
    
    
            //firstView = UIView(frame: .init(origin: .zero, size: .init(width: frame.width, height: 54)))
            //firstView.frame.origin = .zero
            firstView = UIView()
            firstView.backgroundColor = .gray
            firstView.translatesAutoresizingMaskIntoConstraints = false
            scrollView.addSubview(firstView)
    
            //secondView = UIView(frame: firstView.bounds)
            //secondView.frame.origin.x = frame.width
            secondView = UIView()
            secondView.backgroundColor = .lightGray
            secondView.translatesAutoresizingMaskIntoConstraints = false
            scrollView.addSubview(secondView)
    
            addConstraints()
        }
    
        private func addConstraints() {
            NSLayoutConstraint.activate([
    
                // make both subviews equal width and height to scrollView
                firstView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
                firstView.heightAnchor.constraint(equalTo: scrollView.heightAnchor),
                secondView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
                secondView.heightAnchor.constraint(equalTo: scrollView.heightAnchor),
    
                // constrain firstView Leading and Top to scrollView contentLayoutGuide Leading and Top
                firstView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
                firstView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
    
                // constrain secondView Leading to firstView Trailing
                secondView.leadingAnchor.constraint(equalTo: firstView.trailingAnchor),
    
                // constrain secondView Top / Bottom / Trailing Top to scrollView contentLayoutGuide Top / Bottom / Trailing
                secondView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
                secondView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
                secondView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
    
            ])
        }
    
    }