Vadim
Vadim

Reputation: 335

Programmatically add views one after another

I am new to Swift (using 4 version). I have HTML text, that can contain pictures links inside.

some text 
<img src=""/>
some text

I want to make content scrollable, so I wrapped container (simple UIView) into UIScrollview.

Then I parse HTML text into parts: "text" and "image links" into one array (data and type). Then iterating through that array I watch, if the type is text - I input label with constraints:

If it's the first element of the container, then left, top, right, and bottom are match. If not, then top matches the previous view's bottom in a container (like chain).

But for some reason, it's not working. What am I doing wrong? I put my code:

    var lastView = containerView!
    var label : UILabel
    var imageView : UIImageView

    for item in articleContent {
        if (item.type == RenderDetailBlogObject.TYPE_TEXT) {
            label = UILabel()

            label.numberOfLines = 0
            label.attributedText = item.data.html2AttributedString
            containerView.addSubview(label)

            label.translatesAutoresizingMaskIntoConstraints = false

            NSLayoutConstraint.activate([
                label.leftAnchor.constraint(equalTo: containerView.leftAnchor),
                label.rightAnchor.constraint(equalTo: containerView.rightAnchor),
                ])

            if (lastView == containerView) {
                print("ADDING LABEL lastView == containerView")
                label.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0).isActive = true
            } else {
                  print("ADDING LABEL lastView != containerView")
                 label.topAnchor.constraint(equalTo: lastView.bottomAnchor, constant: 0.0).isActive = true
            }

            lastView = label
        } else {
            imageView = UIImageView()

            imageView.sd_setImage(with: URL(string: item.data))
            containerView.addSubview(imageView)

            imageView.translatesAutoresizingMaskIntoConstraints = false

            NSLayoutConstraint.activate([

                imageView.leftAnchor.constraint(equalTo: containerView.leftAnchor),
                imageView.rightAnchor.constraint(equalTo: containerView.rightAnchor),
                ])

            if (lastView == containerView) {
                print("ADDING AN IMAGE lastView == containerView")
                 imageView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0).isActive = true
            } else {
                 print("ADDING AN IMAGE lastView != containerView")
                imageView.topAnchor.constraint(equalTo: lastView.bottomAnchor, constant: 0.0).isActive = true
            }

            lastView = imageView
        }

        lastView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0.0)
    }

EDIT: Code seems to be right and views are adding correctly, but contraints not letting to scroll view.

I tested constraint (center Y - added 200 more range to it and it started to scroll, but it's hardcoded value, so may be it has more legit solution?) Without that constraint it's giving error and not working at all.

enter image description here

Upvotes: 0

Views: 2195

Answers (2)

Fahadsk
Fahadsk

Reputation: 1109

Perhaps a much cleaner approach would be to use a UIStackView inside your scrollview such that you can add any other view as a child view to UIStackView. this way you don't have to deal with the constraints yourself.

So your view hierarchy should be similar to something like below.

->UIView
    ->UIScrollView
        ->UIView
            ->UIStackView

Then add constraints to scrollView as top-left-bottom-right

Also add another UIView to UIScrollView as Wrapper View and add constraint as top-left-bottom-right and add two more constraint to this wrapper view Equals width and Equals height with respect to main view. This will make UIScrollView work with dynamic height, more info in this post

Now add constraint to stack view as top-left-right-bottom also add a height constraint with an outlet connection.

Now when you add any view to the stack view just change the height constant of UIStackView and your UIScrollView will expand itself automatically.

Upvotes: 1

Milan Nos&#225;ľ
Milan Nos&#225;ľ

Reputation: 19737

I believe this line is your source of trouble:

lastView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0.0)

Right now, when you iterate through items, you constrain each label/imageView to have bottom anchor constrained to bottom of the containerView. I would expect that this would cause Unsatisfiable constraints warning (check your console). You want to constrain the bottom of the last label/imageView that you add to the container. Therefore just move that line out of the for loop right behind it:

var lastView = containerView!
var label : UILabel
var imageView : UIImageView

for item in articleContent {
    if (item.type == RenderDetailBlogObject.TYPE_TEXT) {
        label = UILabel()

        label.numberOfLines = 0
        label.attributedText = item.data.html2AttributedString
        containerView.addSubview(label)

        label.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            label.leftAnchor.constraint(equalTo: containerView.leftAnchor),
            label.rightAnchor.constraint(equalTo: containerView.rightAnchor),
            ])

        if lastView == containerView {
            print("ADDING LABEL lastView == containerView")
            label.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0).isActive = true
        } else {
              print("ADDING LABEL lastView != containerView")
            label.topAnchor.constraint(equalTo: lastView.bottomAnchor, constant: 0.0).isActive = true
        }

        lastView = label
    } else {
        imageView = UIImageView()

        imageView.sd_setImage(with: URL(string: item.data))
        containerView.addSubview(imageView)

        imageView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([

            imageView.leftAnchor.constraint(equalTo: containerView.leftAnchor),
            imageView.rightAnchor.constraint(equalTo: containerView.rightAnchor),
            ])

        if lastView == containerView {
            print("ADDING AN IMAGE lastView == containerView")
            imageView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0).isActive = true
        } else {
            print("ADDING AN IMAGE lastView != containerView")
            imageView.topAnchor.constraint(equalTo: lastView.bottomAnchor, constant: 0.0).isActive = true
        }

        lastView = imageView
    }
}
// Constrain the bottom only for the last view that was added
lastView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0.0).isActive = true

If refactoring according to this answer does not solve the scrolling problem, I recommend checking out the constraints between the containerView and the scrollView - you can use this answer as a reference.

Upvotes: 2

Related Questions