ekazaev / ChatLayout

ChatLayout is an alternative solution to MessageKit. It uses custom UICollectionViewLayout to provide you full control over the presentation as well as all the tools available in UICollectionView. It supports dynamic cells and supplementary view sizes.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Image View Play Button Constraints

ElrB opened this issue · comments

commented

Hi,

I hope you are well.
In the example app's ImageView class, I try to create and add a play button to represent video messages, but I am struggling to constrain the play button image view in the centre of the bubble. Sometimes it appears in the centre, sometimes it doesn't (appears misplaced). I have copied the class' code below. Please assist in constraining the play button correctly. Thanks.

final class ImageView: UIView, ContainerCollectionViewCellDelegate {

private lazy var stackView = UIStackView(frame: bounds)

private lazy var loadingIndicator = UIActivityIndicatorView(style: .gray)

private lazy var imageView = UIImageView(frame: bounds)

private lazy var playButtonView = UIImageView()

private var controller: ImageController!

private var imageWidthConstraint: NSLayoutConstraint?

private var imageHeightConstraint: NSLayoutConstraint?

private var viewPortWidth: CGFloat = 300

override init(frame: CGRect) {
    super.init(frame: frame)
    setupSubviews()
}

required init?(coder: NSCoder) {
    super.init(coder: coder)
    setupSubviews()
}

func prepareForReuse() {
    imageView.image = nil
    playButtonView.isHidden = true
}

func apply(_ layoutAttributes: ChatLayoutAttributes) {
    viewPortWidth = layoutAttributes.layoutFrame.width
    setupSize()
}

// Uncomment to demonstrate the manual cell size calculation.
// NB: Keep in mind that the cell itself is still using autolayout to layout it content. If you really want to speed up the
// performance, You must layout the entire!!! `UICollectionCell` manually or using the tools
// like [LayoutKit](https://github.com/linkedin/LayoutKit)

// func preferredLayoutAttributesFitting(_ layoutAttributes: ChatLayoutAttributes) -> ChatLayoutAttributes? {
// viewPortWidth = layoutAttributes.layoutFrame.width
// switch controller.state {
// case .loading:
// layoutAttributes.frame.size.height = 100
// return layoutAttributes
// case let .image(image):
// let maxWidth = min(viewPortWidth * Constants.maxWidth, image.size.width)
// layoutAttributes.frame.size.height = image.size.height * maxWidth / image.size.width
// return layoutAttributes
// }
// }

func setup(with controller: ImageController) {
    self.controller = controller
}

func reloadData() {
    UIView.performWithoutAnimation {
        switch controller.state {
        case .loading:
            loadingIndicator.isHidden = false
            imageView.isHidden = true
            imageView.image = nil
            stackView.removeArrangedSubview(imageView)
            stackView.addArrangedSubview(loadingIndicator)
            if !loadingIndicator.isAnimating {
                loadingIndicator.startAnimating()
            }
            if #available(iOS 13.0, *) {
                backgroundColor = .systemGray5
            } else {
                backgroundColor = UIColor(red: 200 / 255, green: 200 / 255, blue: 200 / 255, alpha: 1)
            }
            setupSize()
        case let .image(image):
            loadingIndicator.isHidden = true
            loadingIndicator.stopAnimating()
            imageView.isHidden = false
            imageView.image = image
            stackView.removeArrangedSubview(loadingIndicator)
            stackView.addArrangedSubview(imageView)
            setupSize()
            backgroundColor = .clear
        }
        
        switch controller.imageType {
        case .videoThumb:
            playButtonView.isHidden = false
        case .image:
            playButtonView.isHidden = true
        }
    }
}

private func setupSubviews() {
    layoutMargins = .zero
    translatesAutoresizingMaskIntoConstraints = false
    insetsLayoutMarginsFromSafeArea = false

    addSubview(stackView)
    addSubview(playButtonView)
    stackView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
        stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
        stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
        stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor)
    ])
    
    if #available(iOS 13.0, *) {
        playButtonView.image = UIImage(systemName: "play.circle.fill")
    }
    
    playButtonView.tintColor = .darkGray
    playButtonView.backgroundColor = .black
    playButtonView.contentMode = .scaleAspectFill
    playButtonView.layer.cornerRadius = 25
    playButtonView.bounds.size = CGSize(width: 50, height: 50)

    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaleAspectFill
    imageView.isHidden = true

    loadingIndicator.translatesAutoresizingMaskIntoConstraints = false
    loadingIndicator.isHidden = true
    
    let loadingWidthConstraint = loadingIndicator.widthAnchor.constraint(equalToConstant: 100)
    loadingWidthConstraint.priority = UILayoutPriority(999)
    loadingWidthConstraint.isActive = true

    let loadingHeightConstraint = loadingIndicator.heightAnchor.constraint(equalToConstant: 100)
    loadingHeightConstraint.priority = UILayoutPriority(999)
    loadingHeightConstraint.isActive = true

    imageWidthConstraint = imageView.widthAnchor.constraint(equalToConstant: 310)
    imageWidthConstraint?.priority = UILayoutPriority(999)

    imageHeightConstraint = imageView.heightAnchor.constraint(equalToConstant: 40)
    imageHeightConstraint?.priority = UILayoutPriority(999)
    playButtonView.center = center
}

private func setupSize() {
    UIView.performWithoutAnimation {
        switch controller.state {
        case .loading:
            imageWidthConstraint?.isActive = false
            imageHeightConstraint?.isActive = false
            playButtonView.center = center
            setNeedsLayout()
        case let .image(image):
            imageWidthConstraint?.isActive = true
            imageHeightConstraint?.isActive = true
            let maxWidth = min(viewPortWidth * Constants.maxWidth, image.size.width)
            imageWidthConstraint?.constant = maxWidth
            imageHeightConstraint?.constant = image.size.height * maxWidth / image.size.width
            playButtonView.center = center
            setNeedsLayout()
        }
    }
}

}

@ElrB Hi. You need to use constraints based layout as the rest of the view relies on constraints. At the moments you set playButtonView.center = center the correct view size has not yet been calculated.

commented

Thank you. The below worked.

playButtonView.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint.activate([
        playButtonView.centerXAnchor.constraint(equalTo: centerXAnchor),
        playButtonView.centerYAnchor.constraint(equalTo: centerYAnchor),
        playButtonView.widthAnchor.constraint(equalToConstant: 50),
        playButtonView.heightAnchor.constraint(equalToConstant: 50)
    ])