How do I make my collectionView UICollectionViewCell a dynamic height? - ios

I have a collectionview and the cells in the collectionview are stretching their height. I used UICollectionViewCompositionalLayout so similar questions asked on this site did not seem to apply.
Here is where I create my UICollectionViewCompositionalLayout
private func configureLayout() -> UICollectionViewCompositionalLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(400))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
return UICollectionViewCompositionalLayout(section: section)
}
Here is the View I am trying to display in the cell.
class PostCell: UICollectionViewCell {
static let reuseIdentifier = String(describing: PostCell.self)
private lazy var textPost : TextPostView = {
let textPost = TextPostView()
textPost.translatesAutoresizingMaskIntoConstraints = false
addSubview(textPost)
textPost.topAnchor.constraint(equalTo: topAnchor).isActive = true
textPost.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
textPost.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
textPost.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
return textPost
}()
private lazy var mediaPost : MediaPostView = {
let mediaPost = MediaPostView()
mediaPost.translatesAutoresizingMaskIntoConstraints = false
addSubview(mediaPost)
mediaPost.topAnchor.constraint(equalTo: topAnchor).isActive = true
mediaPost.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
mediaPost.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
mediaPost.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
return mediaPost
}()
private lazy var eventPost : EventPostView = {
let eventPost = EventPostView()
eventPost.translatesAutoresizingMaskIntoConstraints = false
addSubview(eventPost)
eventPost.topAnchor.constraint(equalTo: topAnchor).isActive = true
eventPost.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
eventPost.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
eventPost.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
return eventPost
}()
func setData(_ presentable: PostPresentable) {
switch presentable.type {
case .text:
eventPost.isHidden = true
mediaPost.isHidden = true
textPost.isHidden = false
textPost.setData(presentable)
case .media:
eventPost.isHidden = true
mediaPost.isHidden = false
textPost.isHidden = true
mediaPost.setData(presentable)
case .event:
eventPost.isHidden = false
mediaPost.isHidden = true
textPost.isHidden = true
eventPost.setData(presentable)
}
}
}
Here is the TextPostView that I display in the cell
import Foundation
import UIKit
class TextPostView: UIView {
lazy var headerView: PostHeaderView = {
let headerView = PostHeaderView()
headerView.translatesAutoresizingMaskIntoConstraints = false
addSubview(headerView)
let margin: CGFloat = 2
headerView.topAnchor.constraint(equalTo: topAnchor, constant: margin).isActive = true
headerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: margin).isActive = true
headerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: margin).isActive = true
return headerView
}()
lazy var descriptionView: UILabel = {
let description = UILabel()
description.translatesAutoresizingMaskIntoConstraints = false
description.font = UIFont.preferredFont(forTextStyle: .body)
description.numberOfLines = 0
let headerViewLayoutGuide = headerView.layoutGuide(self)
addSubview(description)
let margin: CGFloat = 8
description.topAnchor.constraint(equalTo: headerViewLayoutGuide.bottomAnchor, constant: margin).isActive = true
description.leadingAnchor.constraint(equalTo: leadingAnchor,constant: margin).isActive = true
description.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -margin).isActive = true
return description
}()
lazy var footerView: PostFooterView = {
let footer = PostFooterView()
footer.translatesAutoresizingMaskIntoConstraints = false
addSubview(footer)
let descriptionViewLayoutGuide = descriptionView.layoutGuide(self)
footer.topAnchor.constraint(equalTo: descriptionViewLayoutGuide.bottomAnchor, constant: 8).isActive = true
footer.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
footer.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
footer.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
return footer
}()
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initialize()
}
private func initialize() {
layer.borderColor = UIColor.gray.cgColor
layer.borderWidth = 1
layer.cornerRadius = 16
}
func setData(_ presentable: PostPresentable) {
headerView.setData(presentable.header)
footerView.setData(presentable.footer)
descriptionView.text = presentable.description
}
}
The PostHeaderView that the post uses
import Foundation
import UIKit
import SDWebImage
class PostHeaderView: UIView {
private static let horizontalMargin: CGFloat = 8
private static let iconHeight: CGFloat = 20
private lazy var date: UILabel = {
let label = UILabel()
label.font = UIFont.preferredFont(forTextStyle: .caption2)
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: PostHeaderView.horizontalMargin).isActive = true
label.topAnchor.constraint(equalTo: topAnchor).isActive = true
return label
}()
private lazy var flairsContainer: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .fill
stackView.alignment = .trailing
stackView.translatesAutoresizingMaskIntoConstraints = false
let dateLayoutGuide = UILayoutGuide()
let shareLayoutGuide = UILayoutGuide()
addLayoutGuide(dateLayoutGuide)
addLayoutGuide(shareLayoutGuide)
addSubview(stackView)
dateLayoutGuide.trailingAnchor.constraint(equalTo: date.trailingAnchor).isActive = true
shareLayoutGuide.leadingAnchor.constraint(equalTo: shareButton.leadingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: dateLayoutGuide.trailingAnchor, constant: 16).isActive = true
stackView.trailingAnchor.constraint(equalTo: shareLayoutGuide.leadingAnchor, constant: -16).isActive = true
stackView.heightAnchor.constraint(equalToConstant: PostHeaderView.iconHeight).isActive = true
return stackView
}()
private lazy var title: UILabel = {
let title = UILabel()
title.translatesAutoresizingMaskIntoConstraints = false
title.font = UIFont.preferredFont(forTextStyle: .title1)
let avatarLayoutGuide = avatar.layoutGuide(self)
addSubview(title)
title.leadingAnchor.constraint(equalTo: avatarLayoutGuide.trailingAnchor, constant: 4).isActive = true
title.trailingAnchor.constraint(equalTo: trailingAnchor, constant: PostHeaderView.horizontalMargin).isActive = true
title.centerYAnchor.constraint(equalTo: avatarLayoutGuide.centerYAnchor).isActive = true
return title
}()
private lazy var subHeader: UILabel = {
let subHeader = UILabel()
subHeader.translatesAutoresizingMaskIntoConstraints = false
subHeader.font = UIFont.preferredFont(forTextStyle: .caption1)
let titleLayoutGuide = title.layoutGuide(self)
addSubview(subHeader)
subHeader.topAnchor.constraint(equalTo: titleLayoutGuide.bottomAnchor, constant: 0).isActive = true
subHeader.leadingAnchor.constraint(equalTo: titleLayoutGuide.leadingAnchor).isActive = true
return subHeader
}()
private lazy var checkMark: UIImageView = {
let checkMark = UIImageView()
checkMark.translatesAutoresizingMaskIntoConstraints = false
let image = UIImage(named: "VerifiedBadge")
checkMark.image = image
let subHeaderLayoutGuide = subHeader.layoutGuide(self)
addSubview(checkMark)
checkMark.leadingAnchor.constraint(equalTo: subHeaderLayoutGuide.trailingAnchor).isActive = true
checkMark.centerYAnchor.constraint(equalTo: subHeaderLayoutGuide.centerYAnchor).isActive = true
return checkMark
}()
private lazy var menuButton: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "menu"), for: .normal)
addSubview(button)
button.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -PostHeaderView.horizontalMargin).isActive = true
button.topAnchor.constraint(equalTo: topAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: PostHeaderView.iconHeight).isActive = true
return button
}()
private lazy var shareButton: UIButton = {
let shareButton = UIButton()
shareButton.translatesAutoresizingMaskIntoConstraints = false
let buttonImage = UIImage(systemName: "square.and.arrow.up")?.withRenderingMode(.alwaysTemplate)
shareButton.setImage(buttonImage, for: .normal)
shareButton.tintColor = UIColor.black
let menuLayoutGuide = UILayoutGuide()
addLayoutGuide(menuLayoutGuide)
addSubview(shareButton)
menuLayoutGuide.leadingAnchor.constraint(equalTo: menuButton.leadingAnchor).isActive = true
shareButton.trailingAnchor.constraint(equalTo: menuLayoutGuide.leadingAnchor, constant: -16).isActive = true
shareButton.topAnchor.constraint(equalTo: topAnchor).isActive = true
shareButton.heightAnchor.constraint(equalToConstant: 20).isActive = true
return shareButton
}()
private lazy var avatar: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
let imageViewSize: CGFloat = 52
imageView.layer.cornerRadius = imageViewSize / 2
imageView.clipsToBounds = true
let dateLayoutGuide = UILayoutGuide()
addLayoutGuide(dateLayoutGuide)
addSubview(imageView)
dateLayoutGuide.bottomAnchor.constraint(equalTo: date.bottomAnchor).isActive = true
imageView.heightAnchor.constraint(equalToConstant: imageViewSize).isActive = true
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: PostHeaderView.horizontalMargin).isActive = true
imageView.topAnchor.constraint(equalTo: dateLayoutGuide.bottomAnchor, constant: 2).isActive = true
imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func setData(_ presentable: PostHeaderPresentable) {
date.text = presentable.date
avatar.sd_setImage(with: presentable.avatarImageUrl)
title.text = presentable.title
addFlairs(presentable.flairs)
menuButton.isHidden = false
shareButton.isHidden = false
if let subheadline = presentable.subheadline {
subHeader.isHidden = subheadline.text.isEmpty
subHeader.text = subheadline.text
checkMark.isHidden = !subheadline.verified
} else {
checkMark.isHidden = true
subHeader.isHidden = true
}
}
private func addFlairs(_ flairImages: [UIImage]) {
flairsContainer.removeAllArrangedSubviews()
for flairImage in flairImages {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = flairImage
imageView.contentMode = .scaleAspectFill
flairsContainer.addArrangedSubview(imageView)
imageView.heightAnchor.constraint(equalToConstant: PostHeaderView.iconHeight).isActive = true
imageView.widthAnchor.constraint(equalToConstant: PostHeaderView.iconHeight).isActive = true
}
}
}
The PostFooterView
class PostFooterView: UIView {
private static let horizontalMargin: CGFloat = 8
private static let verticalMargin: CGFloat = 16
private lazy var location: UILabel = {
let label = UILabel()
label.font = UIFont.preferredFont(forTextStyle: .subheadline)
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -PostFooterView.horizontalMargin).isActive = true
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -PostFooterView.verticalMargin).isActive = true
return label
}()
private lazy var likeButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
let likeImageSelected = UIImage(named: "Like")?.withRenderingMode(.alwaysTemplate)
let likeImage = UIImage(named: "LikeStroke")?.withRenderingMode(.alwaysTemplate)
button.setImage(likeImageSelected, for: .selected)
button.setImage(likeImage, for: .normal)
button.tintColor = UIColor.black
addSubview(button)
button.leadingAnchor.constraint(equalTo: leadingAnchor, constant: PostFooterView.horizontalMargin).isActive = true
button.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -PostFooterView.verticalMargin).isActive = true
return button
}()
private lazy var dislikeButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
let dislikeImageSelected = UIImage(named: "Dislike")?.withRenderingMode(.alwaysTemplate)
let dislikeImage = UIImage(named: "DislikeStroke")?.withRenderingMode(.alwaysTemplate)
button.setImage(dislikeImageSelected, for: .selected)
button.setImage(dislikeImage, for: .normal)
button.tintColor = UIColor.black
addSubview(button)
let likeButtonLayoutGuide = likeButton.layoutGuide(self)
button.leadingAnchor.constraint(equalTo: likeButtonLayoutGuide.trailingAnchor, constant: 8).isActive = true
button.bottomAnchor.constraint(equalTo: likeButtonLayoutGuide.bottomAnchor).isActive = true
return button
}()
private lazy var navButton : UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
let navigateImage = UIImage(named: "Navigate")?.withRenderingMode(.alwaysTemplate)
button.setImage(navigateImage, for: .normal)
button.setTitle("Navigate", for: .normal)
button.tintColor = UIColor.black
button.setTitleColor(UIColor.black, for: .normal)
addSubview(button)
let likeButtonLayoutGuide = likeButton.layoutGuide(self)
topAnchor.constraint(equalTo: button.topAnchor).isActive = true
button.leadingAnchor.constraint(equalTo: leadingAnchor, constant: PostFooterView.horizontalMargin).isActive = true
button.bottomAnchor.constraint(equalTo: likeButtonLayoutGuide.topAnchor, constant: -20).isActive = true
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func setData(_ presentable: PostFooterPresentable) {
location.text = presentable.location
navButton.isHidden = !presentable.showNavigation
likeButton.isSelected = presentable.reaction == .like
dislikeButton.isSelected = presentable.reaction == .dislike
}
}
Here is a screenshot of how it looks

Related

How to add UIView Gesture - Programmatically UIView

I have searched a lot about adding a gesture on UIView when it is tapped. There are many solutions but none of them worked for me.
I have made UIView class programmatically and using the view in different classes.
Here is my class of UIView:
class PaymentServiceView: UIView {
private let views: UIView = {
let view = UIView()
view.backgroundColor = .white
// view.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner]
view.layer.borderColor = UIColor.gray.cgColor
view.layer.shadowOpacity = 0.3
view.layer.shadowColor = UIColor.gray.cgColor
view.layer.shadowRadius = 10
view.layer.borderWidth = 0.1
view.layer.cornerRadius = 20
view.isUserInteractionEnabled = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private let titleLbl: UILabel = {
var label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.textColor = UIColor.black
label.font = UIFont(name: AppFontName.circularStdRegular, size: 18)
label.clipsToBounds = true
return label
}()
private let subLbl: UILabel = {
var label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.textColor = UIColor.gray
label.numberOfLines = 0
label.textAlignment = .left
label.font = UIFont(name: AppFontName.robotoRegular, size: 15)
label.clipsToBounds = true
return label
}()
private let image: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
private let btnImage: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.image = UIImage(named: IconName.chevron_down)?.transform(withNewColor: UIColor.btnGray)
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
init(titleLabel: String, subTitleLabel: String, imageName: String) {
super.init(frame: CGRect.zero)
self.addSubview(views)
self.views.addSubview(btnImage)
self.views.addSubview(titleLbl)
self.views.addSubview(image)
self.views.addSubview(subLbl)
titleLbl.text = titleLabel
image.image = UIImage(named: imageName)
subLbl.text = subTitleLabel
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupConstraints() {
self.views.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
self.views.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
self.views.heightAnchor.constraint(equalToConstant: 150).isActive = true
self.views.widthAnchor.constraint(equalToConstant: 320).isActive = true
self.image.centerYAnchor.constraint(equalTo: self.views.centerYAnchor).isActive = true
self.image.leadingAnchor.constraint(equalTo: self.views.leadingAnchor, constant: 15).isActive = true
self.image.widthAnchor.constraint(equalToConstant: 30).isActive = true
self.image.heightAnchor.constraint(equalToConstant: 30).isActive = true
self.titleLbl.centerYAnchor.constraint(equalTo: self.views.centerYAnchor, constant: -35).isActive = true
self.titleLbl.leadingAnchor.constraint(equalTo: image.trailingAnchor, constant: 15).isActive = true
self.subLbl.topAnchor.constraint(equalTo: titleLbl.bottomAnchor, constant: 5).isActive = true
self.subLbl.leadingAnchor.constraint(equalTo: image.trailingAnchor, constant: 15).isActive = true
self.subLbl.trailingAnchor.constraint(equalTo: btnImage.leadingAnchor, constant: -15).isActive = true
btnImage.topAnchor.constraint(equalTo: views.topAnchor, constant: 55).isActive = true
btnImage.rightAnchor.constraint(equalTo: views.rightAnchor, constant: -10).isActive = true
btnImage.heightAnchor.constraint(equalToConstant: 10).isActive = true
}
}
Now Im using this UIView class in PaymentServicesViewController
class PaymentServicesViewController: UIViewController {
private (set) lazy var headerView: HeaderView = { [unowned self] in
let view = HeaderView.init(titleLbl: Headings.paymentService, closeAction: {
self.navigationController?.popViewController(animated: true)
}, nextAction: {
print("next")
}, previousAction: {
self.navigationController?.popViewController(animated: true)
})
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private let fundTransferView: PaymentServiceView = {
let view = PaymentServiceView(titleLabel: Headings.fundTransfer, subTitleLabel: Description.fundTransferDecription , imageName: IconName.fundImage )
view.isUserInteractionEnabled = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private let subscriptionView: PaymentServiceView = {
let view = PaymentServiceView(titleLabel: Headings.subscribe, subTitleLabel: Description.subscriptionDescription, imageName: IconName.subImage )
view.isUserInteractionEnabled = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private let billPaymentView: PaymentServiceView = {
let view = PaymentServiceView(titleLabel: Headings.billPayment, subTitleLabel: Description.billPaymentDescription , imageName: IconName.billImage )
view.isUserInteractionEnabled = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray2
let fundtrasnferGesture = UITapGestureRecognizer(target: self, action: #selector(fundTranferTapped))
fundTransferView.isUserInteractionEnabled = true
self.fundTransferView.addGestureRecognizer(fundtrasnferGesture)
let subscribeGesture = UITapGestureRecognizer(target: self, action: #selector(subscribeTapped))
subscriptionView.isUserInteractionEnabled = true
self.subscriptionView.addGestureRecognizer(subscribeGesture)
let billPaymentGesture = UITapGestureRecognizer(target: self, action: #selector(billPaymentTapped))
fundTransferView.isUserInteractionEnabled = true
self.billPaymentView.addGestureRecognizer(billPaymentGesture)
view.addSubview(headerView)
view.addSubview(subscriptionView)
view.addSubview(fundTransferView)
view.addSubview(billPaymentView)
setupConstraint()
}
#objc func fundTranferTapped(sender: UITapGestureRecognizer) {
print("FundTransfer Tapped.")
}
#objc func subscribeTapped(sender: UITapGestureRecognizer) {
print("Subscribe Tapped.")
}
#objc func billPaymentTapped(sender: UITapGestureRecognizer) {
print("BillPayment Tapped.")
}
private func setupConstraint() {
headerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
headerView.heightAnchor.constraint(equalToConstant: 100).isActive = true
subscriptionView.trailingAnchor.constraint(equalTo: fundTransferView.trailingAnchor, constant: 0).isActive = true
subscriptionView.leadingAnchor.constraint(equalTo: fundTransferView.leadingAnchor, constant: 0).isActive = true
subscriptionView.bottomAnchor.constraint(equalTo: fundTransferView.topAnchor, constant: -130).isActive = true
fundTransferView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
fundTransferView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
billPaymentView.leadingAnchor.constraint(equalTo: fundTransferView.leadingAnchor, constant: 0).isActive = true
billPaymentView.trailingAnchor.constraint(equalTo: fundTransferView.trailingAnchor).isActive = true
billPaymentView.topAnchor.constraint(equalTo: fundTransferView.bottomAnchor, constant: 130).isActive = true
}
}
I know there is a small mistake I'm doing but not sure. Help would be appreciated, Thank you.
You are missing a couple constraints...
If you add this at the end of viewDidLoad():
subscriptionView.clipsToBounds = true
fundTransferView.clipsToBounds = true
billPaymentView.clipsToBounds = true
You'll see that the views "disappear":
because they have no Width or Height constraints (so their size is .zero).
In setupConstraints() in your PaymentServiceView class, add these two lines:
self.widthAnchor.constraint(equalTo: self.views.widthAnchor).isActive = true
self.heightAnchor.constraint(equalTo: self.views.heightAnchor).isActive = true
Now, the PaymentServiceView will be the same Width and Height as self.views view.
However, now that the views have Heights:
your layout needs to be adjusted.
Change the .clipsToBounds back to false (remove those added lines) so the shadows won't be clipped, and change these constraints in your controller (adjust the 15 and -15 to your liking):
//subscriptionView.bottomAnchor.constraint(equalTo: fundTransferView.topAnchor, constant: -130).isActive = true
subscriptionView.bottomAnchor.constraint(equalTo: fundTransferView.topAnchor, constant: 15).isActive = true
//billPaymentView.topAnchor.constraint(equalTo: fundTransferView.bottomAnchor, constant: 130).isActive = true
billPaymentView.topAnchor.constraint(equalTo: fundTransferView.bottomAnchor, constant: -15).isActive = true
and we get:
Now, because the views have a size, the Tap Gestures will work.

Pictures disappear when I scroll down in CollectionView

I have a collectionView for display posts, I have 3 different post types (Text,Image and Video). I`ve added an imageview to cell and Im using if else codes in cellForItemAt function for display imageview for Image and Video posts or hide it with heightAnchor = 0 for text posts.
its loading correct at beginning, but when I scroll down and scroll up again images heightAnchor resetting to "0" for every posts. How can I solve this issue ?
When the posts are loaded
When I Scroll down and scroll up again
TimelinePosts CollectionViewCell
class TimelinePosts: UICollectionViewCell {
let avatar: UIImageView = {
let avatar = UIImageView()
avatar.contentMode = .scaleAspectFill
avatar.clipsToBounds = true
avatar.layer.cornerRadius = 24
avatar.translatesAutoresizingMaskIntoConstraints = false
return avatar
}()
let name: UILabel = {
let name = UILabel()
name.numberOfLines = 1
name.translatesAutoresizingMaskIntoConstraints = false
return name
}()
let content: ActiveLabel = {
let content = ActiveLabel()
content.numberOfLines = 0
content.font = UIFont.systemFont(ofSize: 15)
content.translatesAutoresizingMaskIntoConstraints = false
return content
}()
let image: UIImageView = {
let image = UIImageView()
image.contentMode = .scaleAspectFill
image.clipsToBounds = true
image.layer.cornerRadius = 12
image.translatesAutoresizingMaskIntoConstraints = false
return image
}()
let time: UILabel = {
let time = UILabel()
time.numberOfLines = 1
time.textColor = .gray
time.font = UIFont.systemFont(ofSize: 14)
time.textAlignment = .center
time.translatesAutoresizingMaskIntoConstraints = false
return time
}()
let moreButton: UIButton = {
let button = UIButton()
let image = UIImage(named: "arrow")
button.setImage(image, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let favoriteButton: FaveButton = {
let button = FaveButton(frame: CGRect(x:0, y:0, width: 28, height: 28), faveIconNormal: UIImage(named: "favorite"))
button.normalColor = UIColor(hexString: "#CBCBCB")
button.selectedColor = UIColor(hexString: "#FFBE00")
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let boostButton: FaveButton = {
let button = FaveButton(frame: CGRect(x:0, y:0, width: 28, height: 28), faveIconNormal: UIImage(named: "boost-pressed"))
button.normalColor = UIColor(hexString: "#CBCBCB")
button.selectedColor = UIColor(hexString: "#6e00ff")
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let actions: UILabel = {
let view = UILabel()
view.numberOfLines = 1
view.textAlignment = .left
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
addViews()
setupViews()
}
func addViews(){
addSubview(avatar)
addSubview(moreButton)
addSubview(time)
addSubview(name)
addSubview(content)
addSubview(image)
addSubview(favoriteButton)
addSubview(boostButton)
addSubview(actions)
}
func setupViews(){
avatar.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true
avatar.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true
avatar.widthAnchor.constraint(equalToConstant: 48).isActive = true
avatar.heightAnchor.constraint(equalToConstant: 48).isActive = true
moreButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -18).isActive = true
moreButton.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true
moreButton.widthAnchor.constraint(equalToConstant: 14).isActive = true
moreButton.heightAnchor.constraint(equalToConstant: 14).isActive = true
time.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true
time.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
time.widthAnchor.constraint(equalToConstant: 48).isActive = true
time.heightAnchor.constraint(equalToConstant: 20).isActive = true
name.leftAnchor.constraint(equalTo: avatar.rightAnchor, constant: 10).isActive = true
name.rightAnchor.constraint(equalTo: moreButton.leftAnchor, constant: -14).isActive = true
name.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true
name.heightAnchor.constraint(equalToConstant: 20).isActive = true
content.leftAnchor.constraint(equalTo: leftAnchor, constant: 73).isActive = true
content.rightAnchor.constraint(equalTo: rightAnchor, constant: -46).isActive = true
content.topAnchor.constraint(equalTo: name.bottomAnchor, constant: 5).isActive = true
image.leftAnchor.constraint(equalTo: leftAnchor, constant: 73).isActive = true
image.rightAnchor.constraint(equalTo: rightAnchor, constant: -46).isActive = true
image.topAnchor.constraint(equalTo: content.bottomAnchor, constant: 5).isActive = true
image.heightAnchor.constraint(equalToConstant: ((UIScreen.main.bounds.width - 120) * 2) / 3).isActive = true
actions.leftAnchor.constraint(equalTo: avatar.rightAnchor, constant: 10).isActive = true
actions.rightAnchor.constraint(equalTo: moreButton.leftAnchor, constant: -14).isActive = true
actions.topAnchor.constraint(equalTo: image.bottomAnchor, constant: 10).isActive = true
actions.heightAnchor.constraint(equalToConstant: 20).isActive = true
actions.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
favoriteButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -14).isActive = true
favoriteButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
favoriteButton.widthAnchor.constraint(equalToConstant: 20).isActive = true
favoriteButton.heightAnchor.constraint(equalToConstant: 20).isActive = true
boostButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -14).isActive = true
boostButton.bottomAnchor.constraint(equalTo: favoriteButton.topAnchor, constant: -12).isActive = true
boostButton.widthAnchor.constraint(equalToConstant: 20).isActive = true
boostButton.heightAnchor.constraint(equalToConstant: 20).isActive = true
/* name.backgroundColor = .yellow
content.backgroundColor = .red
image.backgroundColor = .green */
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And cellForItemAt function
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TimelinePosts", for: indexPath) as! TimelinePosts
if indexPath.row < id.count{
cell.avatar.sd_setImage(with: URL(string: avatars[indexPath.row]))
cell.time.text = hours[indexPath.row]
cell.favoriteButton.isSelected = isLike[indexPath.row]
cell.favoriteButton.isUserInteractionEnabled = true
cell.favoriteButton.tag = indexPath.row
cell.boostButton.isSelected = isBoost[indexPath.row]
cell.boostButton.isUserInteractionEnabled = true
cell.boostButton.tag = indexPath.row
let selectedArrowTap = UITapGestureRecognizer(target: self, action: #selector(self.selectedArrow))
selectedArrowTap.numberOfTapsRequired = 1
cell.moreButton.isUserInteractionEnabled = true
cell.moreButton.tag = indexPath.row
cell.moreButton.addGestureRecognizer(selectedArrowTap)
cell.content.customize { label in
label.text = content[indexPath.row]
label.hashtagColor = UIColor(hexString: "#6e00ff")
label.mentionColor = UIColor(hexString: "#6e00ff")
label.URLColor = UIColor(hexString: "#0366d6")
}
cell.content.handleHashtagTap { hashtag in
print("Success. You just tapped the \(hashtag) hashtag")
}
cell.content.handleURLTap { url in
let urlString = url.absoluteString
if urlString.hasPrefix("http://")
{
let openURL = URL(string: urlString)!
let svc = SFSafariViewController(url: openURL)
self.present(svc, animated: true, completion: nil)
}
else if urlString.hasPrefix("https://")
{
let openURL = URL(string: urlString)!
let svc = SFSafariViewController(url: openURL)
self.present(svc, animated: true, completion: nil)
}
else
{
let openURL = URL(string: "https://" + urlString)!
let svc = SFSafariViewController(url: openURL)
self.present(svc, animated: true, completion: nil)
}
}
if types[indexPath.row] == 2{
cell.image.sd_setImage(with: URL(string: images[indexPath.row]))
let selectedImageTap = UITapGestureRecognizer(target: self, action: #selector(self.photoZoom))
selectedImageTap.numberOfTapsRequired = 1
cell.image.isUserInteractionEnabled = true
cell.image.tag = indexPath.row
cell.image.addGestureRecognizer(selectedImageTap)
}else if types[indexPath.row] == 3{
cell.image.sd_setImage(with: URL(string: "https://i.ytimg.com/vi/\(self.extractYoutubeIdFromLink(link: videos[indexPath.row])!)/mqdefault.jpg"))
let playBtn = UIImageView()
playBtn.image = UIImage(named: "youtube-play")
playBtn.tag = indexPath.row
playBtn.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.videoPlay)))
playBtn.isUserInteractionEnabled = true
playBtn.translatesAutoresizingMaskIntoConstraints = false
cell.image.isUserInteractionEnabled = true
cell.image.addSubview(playBtn)
playBtn.centerXAnchor.constraint(equalTo: cell.image.centerXAnchor).isActive = true
playBtn.centerYAnchor.constraint(equalTo: cell.image.centerYAnchor).isActive = true
playBtn.widthAnchor.constraint(equalToConstant: 74).isActive = true
playBtn.heightAnchor.constraint(equalToConstant: 52).isActive = true
}else{
cell.image.heightAnchor.constraint(equalToConstant: 0).isActive = true
}
}
return cell
}
you should not modify image height by
cell.image.heightAnchor.constraint(equalToConstant: 0).isActive = true
create a new variable imageHeightConstraint in you TimelinesPosts,
set this variable in setupViews()
imageHeightConstraint = image.heightAnchor.constraint(equalToConstant: ((UIScreen.main.bounds.width - 120) * 2) / 3).isActive = true
then change the height by this
// don't miss this code.
imageHeightConstraint.constant = ((UIScreen.main.bounds.width - 120) * 2) / 3
if types[indexPath.row] == 2 {
//***************
} else if types[indexPath.row] == 3 {
//***************
} else {
imageHeightConstraint.constant = 0
}
UIScrollView is parent UICollectionView. You use UIScrollView with UIScrollViewDelegate for setup images with va scrollOffset: CGFloat follow .y (referent code)
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var scrollOffset : CGFloat = scrollView.contentOffset.y
// process with scrollOffset
}
}

UIButton in a view with animation not detecting touch

I'm following a tutorial to create an interactive popup animation (http://www.swiftkickmobile.com/building-better-app-animations-swift-uiviewpropertyanimator/), and now would like to add buttons to the popup rather than have static text as in the tutorial.
The animation works fine, but the buttons are not detecting touch. Touching the button causes the animation to reverse, instead of printing my "test" statement.
From research, it looks to either be an issue with view hierarchies, the animation disabling the button touch, or layout/constraint issues with the button. I've tried addressing the above issues, but nothing has worked, was hoping someone might be able to help?
I've left out the code pertaining to the animation since I think the issue is to do with layout (and the animation seems to be working fine) - but it's still a lot; apologies in advance for the large amount of code.
class ViewController: UIViewController {
private let popupOffset: CGFloat = 440
private lazy var contentImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "Background")
return imageView
}()
private lazy var overlayView: UIView = {
let view = UIView()
view.backgroundColor = .black
view.alpha = 0
return view
}()
private lazy var popupView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 0.1
view.layer.shadowRadius = 10
return view
}()
private lazy var closedTitleLabel: UILabel = {
let label = UILabel()
label.text = "Hello"
label.font = UIFont.systemFont(ofSize: 16, weight: UIFont.Weight.medium)
label.textColor = UIColor.darkGray
label.textAlignment = .center
return label
}()
private lazy var openTitleLabel: UILabel = {
let label = UILabel()
label.text = "Which door will you choose?"
label.font = UIFont.systemFont(ofSize: 16, weight: UIFont.Weight.medium)
label.textColor = UIColor.darkGray
label.textAlignment = .center
label.alpha = 0
label.transform = CGAffineTransform(scaleX: 1.6, y: 1.6).concatenating(CGAffineTransform(translationX: 0, y: 15))
return label
}()
private lazy var reviewsImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "LabelBackground")
return imageView
}()
let stackView = UIStackView()
let buttonA = UIButton()
let buttonB = UIButton()
let buttonC = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
layout()
popupView.addGestureRecognizer(panRecognizer)
}
override var prefersStatusBarHidden: Bool {
return true
}
//Layout
private var bottomConstraint = NSLayoutConstraint()
private func layout() {
contentImageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(contentImageView)
contentImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
contentImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
contentImageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
contentImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
overlayView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(overlayView)
overlayView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
overlayView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
overlayView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
overlayView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
popupView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(popupView)
popupView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
popupView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
bottomConstraint = popupView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: popupOffset)
bottomConstraint.isActive = true
popupView.heightAnchor.constraint(equalToConstant: 500).isActive = true
closedTitleLabel.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(closedTitleLabel)
closedTitleLabel.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
closedTitleLabel.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
closedTitleLabel.topAnchor.constraint(equalTo: popupView.topAnchor, constant: 20).isActive = true
openTitleLabel.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(openTitleLabel)
openTitleLabel.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
openTitleLabel.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
openTitleLabel.topAnchor.constraint(equalTo: popupView.topAnchor, constant: 20).isActive = true
reviewsImageView.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(reviewsImageView)
reviewsImageView.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
reviewsImageView.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
reviewsImageView.bottomAnchor.constraint(equalTo: popupView.bottomAnchor).isActive = true
reviewsImageView.heightAnchor.constraint(equalToConstant: 428).isActive = true
buttonA.backgroundColor = UIColor.clear
let heightConstraintA = buttonA.heightAnchor.constraint(equalToConstant: 135)
heightConstraintA.isActive = true
heightConstraintA.priority = UILayoutPriority(rawValue: 999)
buttonA.translatesAutoresizingMaskIntoConstraints = false
buttonA.setTitle("A", for: .normal)
buttonA.setTitleColor(UIColor.darkGray, for: .normal)
buttonA.backgroundColor = UIColor.clear
buttonA.addTarget(self, action: #selector(buttonATapped(sender:)), for: .touchDown)
//self.popupView.addSubview(buttonA)
buttonB.backgroundColor = UIColor.clear
let heightConstraintB = buttonB.heightAnchor.constraint(equalToConstant: 135)
heightConstraintB.isActive = true
heightConstraintB.priority = UILayoutPriority(rawValue: 999)
buttonB.translatesAutoresizingMaskIntoConstraints = false
buttonB.setTitle("B", for: .normal)
buttonB.setTitleColor(UIColor.darkGray, for: .normal)
buttonB.backgroundColor = UIColor.clear
//self.popupView.addSubview(buttonB)
buttonC.backgroundColor = UIColor.clear
let heightConstraintC = buttonC.heightAnchor.constraint(equalToConstant: 135)
heightConstraintC.isActive = true
heightConstraintC.priority = UILayoutPriority(rawValue: 999)
buttonC.translatesAutoresizingMaskIntoConstraints = false
buttonC.setTitle("C", for: .normal)
buttonC.setTitleColor(UIColor.darkGray, for: .normal)
buttonC.backgroundColor = UIColor.clear
//self.popupView.addSubview(buttonC)
popupView.addSubview(stackView)
stackView.backgroundColor = UIColor.clear
stackView.addArrangedSubview(buttonA)
stackView.addArrangedSubview(buttonB)
stackView.addArrangedSubview(buttonC)
stackView.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: popupView.bottomAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 428).isActive = true
stackView.axis = .vertical
stackView.distribution = .fill
stackView.translatesAutoresizingMaskIntoConstraints = false
}
#objc func buttonATapped(sender: UIButton) {
print ("test")
}
private func animateTransitionIfNeeded(to state: State, duration: TimeInterval) {
//Animation code
}
#objc private func popupViewPanned(recognizer: UIPanGestureRecognizer) {
//Animation code
}
}
***For anyone else having the same issue, here is how I solved it, thanks to #OverD:
I removed the reviewsImageView completely (because that was only for color in my case, and I can easily add the color to the UIButton instead) Then instead of adding the buttons to the popupView, I added them to the stack view, and the stack view to the popupView. Lastly, my syntax for addTarget was not correct, and for some reason changing it to touchDown instead of touchUpInside made it work.
I noticed from the provided code that you are adding the same buttons multiple times, once as a subview of the popView and once in the stackView. Also you are not assigning any targets for the buttons.
I hope this helps

Swift: How to programmatically create reusable views?

Let's say I am describing my own UIView, let's call it a HeaderView. I want the HeaderView to have the exact same properties, but only differ in label text. Here is how I currently have it:
private let headerView: UIView = {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
let view = UIView()
view.backgroundColor = .white
view.heightAnchor.constraint(equalToConstant: 65).isActive = true
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 30)
label.textAlignment = .left
label.textColor = .black
label.text = "Search"
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8).isActive = true
label.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 5).isActive = true
return view
}()
How I'd use it:
view.addSubview(headerView)
headerView.translatesAutoresizingMaskIntoConstraints = false
headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
headerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
What if I want 3 of these header views with varying text? How would I make it into a reusable programmatic view?
You can create a subclass of UIView and reuse it anywhere
class HeaderView: UIView {
let innerview = UIView()
let innerlabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
sharedLayout()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
sharedLayout()
}
private func sharedLayout() {
self.addSubview(innerview)
self.innerView.backgroundColor = UIColor.red
innerview.translatesAutoresizingMaskIntoConstraints = false
innerview.trailingAnchor.constraint(equalTo:self.trailingAnchor).isActive = true
innerview.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
innerview.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
innerview.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
// configure other items here
}
}
Instead of a variable you could have a function:
func buildHeaderView(withText text: String) -> UIView {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
let view = UIView()
view.backgroundColor = .white
view.heightAnchor.constraint(equalToConstant: 65).isActive = true
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 30)
label.textAlignment = .left
label.textColor = .black
label.text = text
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8).isActive = true
label.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 5).isActive = true
return view
}
Now you could use this function like:
let searchHeaderView = buildHeaderView(withText: "Search")
view.addSubview(searchHeaderView)
let otherView = buildHeaderView(withText: "Other")

Nothing happens when clicking UIButton

i've created a subclass of UIView which contain a SwitchButton, however even though i have added a target there is still no reaction/print when i tap the button. How come is that?
viewController
self.tradeView = TradeView()
self.tradeView.translatesAutoresizingMaskIntoConstraints = false
self.tradeView.backgroundColor = Color.theme.value
self.view.addSubview(self.tradeView)
subclass
class TradeView: UIView {
var waveView: UIImageView!
var bottomView: UIView!
var topView: UIView!
var centerView: UIView!
var switchButton: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
setUp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
func setUp() {
waveView = UIImageView()
waveView.translatesAutoresizingMaskIntoConstraints = false
waveView.isUserInteractionEnabled = false
waveView.image = UIImage(named: "wave")
self.addSubview(waveView)
topView = UIImageView()
topView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(topView)
centerView = UIImageView()
centerView.isUserInteractionEnabled = false
centerView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(centerView)
bottomView = UIImageView()
bottomView.isUserInteractionEnabled = false
bottomView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(bottomView)
waveView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 1).isActive = true
waveView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
waveView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
waveView.heightAnchor.constraint(equalToConstant: 32).isActive = true
waveView.topAnchor.constraint(equalTo: self.bottomView.bottomAnchor).isActive = true
topView.bottomAnchor.constraint(equalTo: self.centerView.topAnchor).isActive = true
topView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
topView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
topView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
topView.heightAnchor.constraint(equalTo: bottomView.heightAnchor).isActive = true
centerView.bottomAnchor.constraint(equalTo: self.bottomView.topAnchor).isActive = true
centerView.topAnchor.constraint(equalTo: self.topView.bottomAnchor).isActive = true
centerView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
centerView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
centerView.heightAnchor.constraint(equalToConstant: 46).isActive = true
bottomView.bottomAnchor.constraint(equalTo: self.waveView.topAnchor).isActive = true
bottomView.topAnchor.constraint(equalTo: self.centerView.bottomAnchor).isActive = true
bottomView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
bottomView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
bottomView.heightAnchor.constraint(equalTo: self.topView.heightAnchor).isActive = true
setUpCenterView()
}
func setUpCenterView() {
let borderView = UIView()
borderView.translatesAutoresizingMaskIntoConstraints = false
borderView.backgroundColor = Color.lightButton.withAlpha(0.3)
self.centerView.addSubview(borderView)
self.switchButton = UIButton(type: .custom)
switchButton.translatesAutoresizingMaskIntoConstraints = false
self.switchButton.setImage(UIImage(named: "switch"), for: .normal)
self.switchButton.addTarget(self, action: #selector(switchExchange), for: UIControlEvents.touchUpInside)
self.centerView.addSubview(switchButton)
borderView.centerYAnchor.constraint(equalTo: self.centerView.centerYAnchor).isActive = true
borderView.leftAnchor.constraint(equalTo: self.centerView.leftAnchor, constant: 24).isActive = true
borderView.rightAnchor.constraint(equalTo: self.switchButton.leftAnchor, constant: -24).isActive = true
borderView.heightAnchor.constraint(equalToConstant: 1).isActive = true
self.switchButton.centerYAnchor.constraint(equalTo: self.centerView.centerYAnchor).isActive = true
self.switchButton.leftAnchor.constraint(equalTo: borderView.rightAnchor, constant: 24).isActive = true
self.switchButton.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -24).isActive = true
self.switchButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
self.switchButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
}
#objc func switchExchange() {
print("swap")
(bottomObj,topObj) = (topObj,bottomObj)
}
}
Your centerView has isUserInteractionEnabled set to false. This prevents all its subviews, including switchButton, from interacting.
Replace
centerView.isUserInteractionEnabled = false
with
centerView.isUserInteractionEnabled = true

Resources