0
votes

I create my views programmatically without using storyboard. And I get this issue "runtime: Layout Issues: Scrollable content size is ambiguous for UIScrollView" from Xcode when debug view hierarchy is opened. I think it is mean that the constraints on scollView is unclear.

I could see the scrollView is showed (yellow color one) in view hierarchy, but its subview UIView is not. Find some solution with storyboard/IB, though I couldn't connect that with my code. Any hint and advice is appreciated.

enter image description here

Below is the scrollView's structure.

ScrollView
  ->UIView as contentView(coverImageContainer)
      -> UIImageView(coverArtImageView)
      -> UIButton
// scroller
    lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false
//        scrollView.contentMode = .scaleToFill
        scrollView.contentInsetAdjustmentBehavior = .never
        scrollView.alwaysBounceVertical = true
        scrollView.backgroundColor = .yellow
        
        return scrollView
    }()
    
    // cover image
    lazy var coverImageContainer: UIView = {
        let containerView = UIView()
        containerView.translatesAutoresizingMaskIntoConstraints = false
        return containerView
    }()
    
    lazy var coverArtImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFit
        imageView.layer.cornerRadius = 5
        return imageView
    }()
    
    lazy var dismissChevron: UIButton = {
        let btn = UIButton()
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.addTarget(self, action: #selector(disimissAction), for: .touchUpInside)
        return btn
    }()

override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .black
        backingImageView.image = backingImage
        
        view.addSubview(backingImageView)
        view.addSubview(dimmerLayer)
        view.addSubview(scrollView)
        scrollView.addSubview(coverImageContainer)
        scrollView.addSubview(stretchySkirt)
        coverImageContainer.addSubview(coverArtImageView)
        coverImageContainer.addSubview(dismissChevron)
        
        // set layout constraint
        configureLayout()
        ...        

    }
func configureLayout() {
        
        let g = view.safeAreaLayoutGuide
        backingImageleadInset = backingImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor)
        backingImageTrailingInset = backingImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor)
        backingImageTopInset = backingImageView.topAnchor.constraint(equalTo: view.topAnchor)
        backingImageBottomInset = backingImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        
        NSLayoutConstraint.activate([

            backingImageleadInset,
            backingImageTrailingInset,
            backingImageTopInset,
            backingImageBottomInset,
            
            dimmerLayer.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            dimmerLayer.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            dimmerLayer.topAnchor.constraint(equalTo: view.topAnchor),
            dimmerLayer.bottomAnchor.constraint(equalTo: view.bottomAnchor),

            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: backingImageView.topAnchor, constant: 15),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),

            coverImageContainer.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            coverImageContainer.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 57),
            coverImageContainer.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            coverImageContainer.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            coverImageContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -237),

            stretchySkirt.topAnchor.constraint(equalTo: coverImageContainer.bottomAnchor),
            stretchySkirt.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            stretchySkirt.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            stretchySkirt.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),

            coverArtImageView.centerXAnchor.constraint(equalTo: coverImageContainer.centerXAnchor),
            coverArtImageView.topAnchor.constraint(equalTo: coverImageContainer.topAnchor, constant: 38),
            coverArtImageView.leadingAnchor.constraint(equalTo: coverImageContainer.leadingAnchor, constant: 20),
            coverArtImageView.trailingAnchor.constraint(equalTo: coverImageContainer.trailingAnchor, constant: -20),
//            coverArtImageView.widthAnchor.constraint(equalToConstant: 354),
            coverArtImageView.heightAnchor.constraint(equalTo: coverArtImageView.widthAnchor),

            dismissChevron.centerXAnchor.constraint(equalTo: coverImageContainer.centerXAnchor),
            dismissChevron.topAnchor.constraint(equalTo: coverImageContainer.topAnchor, constant: 4),
        ])
    }
1
I think this link is useful for you stackoverflow.com/a/59903579/5140621hessam mahdiabadi
Your constraints do not make clear what you really want... You have a coverImageContainer that should be the full width of the scroll view - what should its Height be? You indicate you want its Bottom to be -237, but relative to what? Is that to allow it to scroll up?DonMag
Hi @DonMag, coverImageContainer's bottom is related to its superView(scrollView)'s bottom and with an offset -237. I think that would be ok.ChuckZHB
@hessam mahdiabadi, thanks I'm looking into it.ChuckZHB
@ChuckZHB - "related to its superView(scrollView)'s bottom" ... do you mean the "scrollable content area" or the "scrollView's FRAME"?DonMag

1 Answers

0
votes

As DongMag's comment, I fix the issue now.

What I want to achieve is as below screenshot illustrated. It is like Apple music's Max Card View.

enter image description here

Well, the problem is on coverImageContainer's constraints, I should constraint it to view or view.safeAreaLayoutGuide but not scrollView. Because I don't want coverImageContainer to scroll.

Update my code here, if anyone meet the same issue in the future, they may do a reference:

let g = view.safeAreaLayoutGuide 
NSLayoutConstraint.activate([
            . . . 
            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: backingImageView.topAnchor, constant: 15),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),

            coverImageContainer.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            coverImageContainer.topAnchor.constraint(equalTo: g.topAnchor, constant: 57),
            coverImageContainer.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            coverImageContainer.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            coverImageContainer.heightAnchor.constraint(equalTo: coverImageContainer.widthAnchor),

            stretchySkirt.topAnchor.constraint(equalTo: coverImageContainer.bottomAnchor),
            stretchySkirt.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            stretchySkirt.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            stretchySkirt.bottomAnchor.constraint(equalTo: view.bottomAnchor),

            coverArtImageView.centerXAnchor.constraint(equalTo: coverImageContainer.centerXAnchor),
            coverArtImageView.topAnchor.constraint(equalTo: coverImageContainer.topAnchor, constant: 38),
            coverArtImageView.leadingAnchor.constraint(equalTo: coverImageContainer.leadingAnchor, constant: 38),
            coverArtImageView.heightAnchor.constraint(equalTo: coverArtImageView.widthAnchor),

            dismissChevron.centerXAnchor.constraint(equalTo: coverImageContainer.centerXAnchor),
            dismissChevron.topAnchor.constraint(equalTo: coverImageContainer.topAnchor, constant: 4),
        ])