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.
Related
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
I have an iOS application where i have a textfield and a button and on tap of button i have to hide the textfield.
I am setting heightAnchor to 0 on tap of button. Everything is working fine on iOS 14(14.5) but does not work(does not hide the text field) on iOS 15. Also, I have tried setting up the isHidden property on UITextField but it does not work.
Can you please help tell if something changed or i am doing something wrong. Thank you.
Code reference:
import UIKit
class ViewController: UIViewController {
private lazy var mytextFeild: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
var heightConstraint: NSLayoutConstraint?
#objc func buttonTapped() {
heightConstraint?.isActive = false
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 0)
heightConstraint?.isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mytextFeild)
view.addSubview(testView)
view.addSubview(button)
mytextFeild.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32).isActive = true
mytextFeild.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
mytextFeild.topAnchor.constraint(equalTo: view.topAnchor, constant: 64).isActive = true
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 32.0)
heightConstraint?.isActive = true
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32.0).isActive = true
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -64.0).isActive = true
testView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
testView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
testView.topAnchor.constraint(equalTo: mytextFeild.bottomAnchor).isActive = true
testView.bottomAnchor.constraint(equalTo: button.topAnchor).isActive = true
}
}
Add them to a stack then add stack to the viewController. at the end try to hide it easily without changing the height.
class ViewController: UIViewController {
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .center
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private lazy var myTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
#objc func buttonTapped() {
myTextField.isHidden = !myTextField.isHidden
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
stackView.addArrangedSubview(myTextField)
stackView.addArrangedSubview(testView)
stackView.addArrangedSubview(button)
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -64.0).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
myTextField.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
myTextField.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
testView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
}
}
I have a vertical UIScrollView, allScrollView, and a horizontal UIScrollView, hourlyScrollView, nested inside the vertical UIScrollView. In each of the scroll views I have 10 other UIViews that will show data. Each of the views are assigned a UITapGestureRecognizer. I'm able to tap the views that are only in the vertical scroll, but none of the views in the nested horizontal scroll do anything when tapped. If anyone can help me it would be greatly appreciated as I've tried a lot of suggestions on here with no luck.
my view hierarchy:
-allScrollView (vertical)
-allContentView
-hourlyScrollView (horizontal)
-hourlyContentView
-10 UIViews
-dailyContentView
-10 UIViews
viewDidLoad()
let dailyContentView = UIView()
let hourlyContentView = UIView()
let hourlyScrollView = UIScrollView()
let allContentView = UIView()
let allScrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
hourlyScrollView.translatesAutoresizingMaskIntoConstraints = false
hourlyContentView.translatesAutoresizingMaskIntoConstraints = false
allContentView.translatesAutoresizingMaskIntoConstraints = false
allScrollView.translatesAutoresizingMaskIntoConstraints = false
dailyContentView.translatesAutoresizingMaskIntoConstraints = false
layoutHourlyViews()
layoutDailyViews()
finishLayout()
}
create horizontal scroll content
func layoutHourlyViews() {
for subview in hourlyScrollView.subviews {
subview.removeFromSuperview()
}
for subView in hourlyContentView.subviews {
subView.removeFromSuperview()
}
var previousHour : UIView? = nil
for count in 1...10 {
let hourlyUIView = UIView()
hourlyUIView.backgroundColor = .blue
hourlyUIView.isUserInteractionEnabled = true
hourlyUIView.tag = count
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hourlyTap(_:)))
hourlyUIView.addGestureRecognizer(tapGesture)
let descriptionLabel = UILabel()
let borderView = UIView()
let borderViewTop = UIView()
let borderViewBottom = UIView()
borderViewTop.backgroundColor = .black
borderViewBottom.backgroundColor = .black
borderViewTop.translatesAutoresizingMaskIntoConstraints = false
borderViewBottom.translatesAutoresizingMaskIntoConstraints = false
borderView.translatesAutoresizingMaskIntoConstraints = false
hourlyUIView.translatesAutoresizingMaskIntoConstraints = false
descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
descriptionLabel.text = "test"
borderView.backgroundColor = .black
hourlyUIView.addSubview(borderViewBottom)
hourlyUIView.addSubview(borderViewTop)
hourlyUIView.addSubview(descriptionLabel)
hourlyUIView.addSubview(borderView)
borderViewBottom.leadingAnchor.constraint(equalTo: hourlyUIView.leadingAnchor).isActive = true
borderViewBottom.trailingAnchor.constraint(equalTo: hourlyUIView.trailingAnchor).isActive = true
borderViewBottom.bottomAnchor.constraint(equalTo: hourlyUIView.bottomAnchor).isActive = true
borderViewBottom.heightAnchor.constraint(equalToConstant: 2).isActive = true
borderViewTop.leadingAnchor.constraint(equalTo: hourlyUIView.leadingAnchor).isActive = true
borderViewTop.trailingAnchor.constraint(equalTo: hourlyUIView.trailingAnchor).isActive = true
borderViewTop.topAnchor.constraint(equalTo: hourlyUIView.topAnchor).isActive = true
borderViewTop.heightAnchor.constraint(equalToConstant: 2).isActive = true
hourlyUIView.widthAnchor.constraint(equalToConstant: 160).isActive = true
hourlyUIView.heightAnchor.constraint(equalToConstant: 240).isActive = true
descriptionLabel.topAnchor.constraint(equalTo: hourlyUIView.topAnchor, constant: 16).isActive = true
descriptionLabel.centerXAnchor.constraint(equalTo: hourlyUIView.centerXAnchor, constant: 0).isActive = true
hourlyContentView.addSubview(hourlyUIView)
if previousHour == nil {
hourlyUIView.topAnchor.constraint(equalTo: hourlyContentView.topAnchor, constant: 0).isActive = true
hourlyUIView.leadingAnchor.constraint(equalTo: hourlyContentView.leadingAnchor, constant: 2).isActive = true
}
else {
hourlyUIView.topAnchor.constraint(equalTo: hourlyContentView.topAnchor, constant: 0).isActive = true
hourlyUIView.leadingAnchor.constraint(equalTo: previousHour!.trailingAnchor, constant: 0).isActive = true
borderView.bottomAnchor.constraint(equalTo: hourlyUIView.bottomAnchor).isActive = true
borderView.topAnchor.constraint(equalTo: hourlyUIView.topAnchor).isActive = true
borderView.widthAnchor.constraint(equalToConstant: 2).isActive = true
borderView.leadingAnchor.constraint(equalTo: hourlyUIView.leadingAnchor).isActive = true
}
previousHour = hourlyUIView
}
let borderViewTop = UIView()
let borderViewBottom = UIView()
borderViewTop.backgroundColor = .black
borderViewBottom.backgroundColor = .black
borderViewTop.translatesAutoresizingMaskIntoConstraints = false
borderViewBottom.translatesAutoresizingMaskIntoConstraints = false
hourlyScrollView.addSubview(hourlyContentView)
hourlyContentView.leadingAnchor.constraint(equalTo: hourlyScrollView.leadingAnchor, constant: 0).isActive = true
hourlyContentView.topAnchor.constraint(equalTo: hourlyScrollView.topAnchor, constant: 0).isActive = true
hourlyContentView.trailingAnchor.constraint(equalTo: hourlyScrollView.trailingAnchor, constant: 0).isActive = true
hourlyContentView.bottomAnchor.constraint(equalTo: hourlyScrollView.bottomAnchor, constant: 0).isActive = true
allContentView.addSubview(hourlyScrollView)
hourlyScrollView.isScrollEnabled = true
hourlyScrollView.topAnchor.constraint(equalTo: allContentView.topAnchor, constant: 20).isActive = true
hourlyScrollView.leadingAnchor.constraint(equalTo: allContentView.leadingAnchor, constant: 0).isActive = true
hourlyScrollView.trailingAnchor.constraint(equalTo: allContentView.trailingAnchor, constant: 0).isActive = true
}
create vertical scroll content
func layoutDailyViews() {
for subview in dailyContentView.subviews {
subview.removeFromSuperview()
}
var previousDay : UIView? = nil
for count in 1...10 {
let dailyUIView = UIView()
//dailyUIView.isUserInteractionEnabled = true
dailyUIView.tag = count
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dailyTap(_:)))
dailyUIView.addGestureRecognizer(tapGesture)
//hourlyUIView.frame.size = CGSize(width: 500, height: 50)
let descriptionLabel = UILabel()
dailyUIView.translatesAutoresizingMaskIntoConstraints = false
descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
descriptionLabel.text = "daily test"
let borderView = UIView()
borderView.translatesAutoresizingMaskIntoConstraints = false
borderView.backgroundColor = .black
dailyUIView.addSubview(descriptionLabel)
dailyUIView.addSubview(borderView)
borderView.bottomAnchor.constraint(equalTo: dailyUIView.bottomAnchor).isActive = true
borderView.heightAnchor.constraint(equalToConstant: 2).isActive = true
borderView.leadingAnchor.constraint(equalTo: dailyUIView.leadingAnchor).isActive = true
borderView.trailingAnchor.constraint(equalTo: dailyUIView.trailingAnchor).isActive = true
dailyUIView.heightAnchor.constraint(equalToConstant: 100).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: dailyUIView.leadingAnchor, constant: 16).isActive = true
descriptionLabel.centerYAnchor.constraint(equalTo: dailyUIView.centerYAnchor, constant: 0).isActive = true
dailyContentView.addSubview(dailyUIView)
if previousDay == nil {
dailyUIView.topAnchor.constraint(equalTo: dailyContentView.topAnchor, constant: 0).isActive = true
}
else {
dailyUIView.topAnchor.constraint(equalTo: previousDay!.bottomAnchor, constant: 0).isActive = true
}
dailyUIView.widthAnchor.constraint(equalToConstant: view.frame.width - 4).isActive = true
dailyUIView.centerXAnchor.constraint(equalTo: dailyContentView.centerXAnchor).isActive = true
previousDay = dailyUIView
}
allContentView.addSubview(dailyContentView)
}
func finishLayout() {
hourlyScrollView.bottomAnchor.constraint(equalTo: dailyContentView.bottomAnchor, constant: 0).isActive = true
dailyContentView.topAnchor.constraint(equalTo: allContentView.topAnchor, constant: 260).isActive = true
dailyContentView.centerXAnchor.constraint(equalTo: allContentView.centerXAnchor, constant: 0).isActive = true
dailyContentView.leadingAnchor.constraint(equalTo: allContentView.leadingAnchor).isActive = true
dailyContentView.trailingAnchor.constraint(equalTo: allContentView.trailingAnchor, constant: 0).isActive = true
dailyContentView.bottomAnchor.constraint(equalTo: allContentView.bottomAnchor).isActive = true
allScrollView.addSubview(allContentView)
allContentView.topAnchor.constraint(equalTo: allScrollView.topAnchor).isActive = true
allContentView.leadingAnchor.constraint(equalTo: allScrollView.leadingAnchor).isActive = true
allContentView.trailingAnchor.constraint(equalTo: allScrollView.trailingAnchor).isActive = true
allContentView.bottomAnchor.constraint(equalTo: allScrollView.bottomAnchor).isActive = true
allContentView.centerXAnchor.constraint(equalTo: allScrollView.centerXAnchor).isActive = true
allContentView.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
allContentView.heightAnchor.constraint(equalToConstant: 1500).isActive = true
view.addSubview(allScrollView)
allScrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
allScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
allScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
allScrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
tapped functions
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
hourlyScrollView.contentSize = CGSize(width:160 * 10 + 2, height:240)
allScrollView.contentSize = CGSize(width: view.frame.width, height:1500)
}
#objc func hourlyTap(_ sender: UITapGestureRecognizer) {
let tappedView = sender.view
print("hourly: \(tappedView!.tag)")
}
#objc func dailyTap(_ sender: UITapGestureRecognizer) {
let tappedView = sender.view
print("daily: \(tappedView!.tag)")
}
You haven't set all the required constraints on your hourlyContentView so the horizontal scrollView's size is (0,0) and as such it can't be scrolled or tapped. You can use Debug View Hierarchy in Xcode to see this.
The constraints you need to add are:
Between your last hourlyUIView's trailingAnchor and hourlyContentView's trailing anchor:
...
previousHour = hourlyUIView
}
previousHour?.trailingAnchor.constraint(equalTo: hourlyContentView.trailingAnchor).isActive = true
let borderViewTop = UIView()
let borderViewBottom = UIView()
...
Setting your hourlyContentView heightAnchor equal to the hourlyScrollView height anchor:
hourlyContentView.heightAnchor.constraint(equalTo: hourlyScrollView.heightAnchor).isActive = true
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
Conditions:
Swift 4, Xcode 9.3
Target: iOS 11.3
UI Done Programatically
Using Constraints
My Root View Controller is a Navigation
Situation:
I wanted to float an audio player that will be visible throughout the app.
I did an AudioPlayer.swift class that contains the user interface of the audio player.
AudioPlayer.swift
import Foundation
import UIKit
import FRadioPlayer
class AudioPlayer: UIView {
let screenSize: CGRect = UIScreen.main.bounds
let playerImage: UIImageView = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = false
iv.contentMode = .scaleAspectFill
iv.layer.masksToBounds = true
return iv
}()
let playerTitle: UILabel = {
let l = UILabel()
l.textColor = .darkGray
l.font = UIFont.boldSystemFont(ofSize: 13)
l.translatesAutoresizingMaskIntoConstraints = false
return l
}()
let playerSeriesTitle: UILabel = {
let l = UILabel()
l.textColor = .darkGray
l.font = UIFont.boldSystemFont(ofSize: 12)
l.translatesAutoresizingMaskIntoConstraints = false
return l
}()
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
setupAudioControls()
}
private func setupAudioControls(){
let appDelegate = AppDelegate.sharedInstance
self.backgroundColor = UIColor.init(hex: "#EBE4D3")
self.addSubview(playerImage)
self.addSubview(playerTitle)
self.addSubview(playerSeriesTitle)
self.heightAnchor.constraint(equalToConstant: 150).isActive = true
self.bottomAnchor.constraint(equalTo: appDelegate().rootView ).isActive = true
self.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor).isActive = true
self.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor).isActive = true
playerImage.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
playerImage.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10).isActive = true
playerImage.widthAnchor.constraint(equalToConstant: 55).isActive = true
playerImage.heightAnchor.constraint(equalToConstant: 55).isActive = true
playerTitle.topAnchor.constraint(equalTo: self.topAnchor, constant: 5).isActive = true
playerTitle.leadingAnchor.constraint(equalTo: playerImage.trailingAnchor, constant: 10).isActive = true
playerTitle.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 10).isActive = true
playerTitle.heightAnchor.constraint(equalToConstant: 25).isActive = true
playerSeriesTitle.topAnchor.constraint(equalTo: playerTitle.topAnchor, constant: 20).isActive = true
playerSeriesTitle.leadingAnchor.constraint(equalTo: playerImage.trailingAnchor, constant: 10).isActive = true
playerSeriesTitle.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 10).isActive = true
playerSeriesTitle.heightAnchor.constraint(equalToConstant: 20).isActive = true
UIView.animate(withDuration: 0.5, animations: {
self.frame.origin.y -= 150
self.playerImage.frame.origin.y -= 150
self.playerTitle.frame.origin.y -= 150
self.playerSeriesTitle.frame.origin.y -= 150
}, completion: nil)
self.setNeedsLayout()
self.reloadInputViews()
}
}
Problem:
How can I add this to the Root View Controller to stay on top in all view controllers that I have in my app? Wherever I navigate, the player must stay on the bottom part of every controller. As you can see, I need a reference to the rootviewcontroller to set the contraints for the AudioPlayer but I failed in so many attempts (like calling the rootviewcontroller using AppDelegate)
I update it for you
add singleton static let shared = AudioPlayer()
add public func showAudioPlayer () --> to display Audio player
add as subview to UIApplication.shared.keyWindow?
TODO- add HideAudioPlayer()
Use like this
AudioPlayer.shared.showAudioPlayer()
Here is updated code
import Foundation
import UIKit
class AudioPlayer: UIView {
static let shared = AudioPlayer()
let screenSize: CGRect = UIScreen.main.bounds
let playerImage: UIImageView = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = false
iv.contentMode = .scaleAspectFill
iv.layer.masksToBounds = true
return iv
}()
let playerTitle: UILabel = {
let l = UILabel()
l.textColor = .darkGray
l.font = UIFont.boldSystemFont(ofSize: 13)
l.translatesAutoresizingMaskIntoConstraints = false
return l
}()
let playerSeriesTitle: UILabel = {
let l = UILabel()
l.textColor = .darkGray
l.font = UIFont.boldSystemFont(ofSize: 12)
l.translatesAutoresizingMaskIntoConstraints = false
return l
}()
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
// setupAudioControls()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func showAudioPlayer (){
self.setupAudioControls()
}
private func setupAudioControls(){
self.backgroundColor = .red
self.addSubview(playerImage)
self.addSubview(playerTitle)
self.addSubview(playerSeriesTitle)
UIApplication.shared.keyWindow?.addSubview(self)
if let layoutGuide = UIApplication.shared.keyWindow?.layoutMarginsGuide {
self.heightAnchor.constraint(equalToConstant: 150).isActive = true
self.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor ).isActive = true
self.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor).isActive = true
self.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor).isActive = true
}
playerImage.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
playerImage.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10).isActive = true
playerImage.widthAnchor.constraint(equalToConstant: 55).isActive = true
playerImage.heightAnchor.constraint(equalToConstant: 55).isActive = true
playerTitle.topAnchor.constraint(equalTo: self.topAnchor, constant: 5).isActive = true
playerTitle.leadingAnchor.constraint(equalTo: playerImage.trailingAnchor, constant: 10).isActive = true
playerTitle.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 10).isActive = true
playerTitle.heightAnchor.constraint(equalToConstant: 25).isActive = true
playerSeriesTitle.topAnchor.constraint(equalTo: playerTitle.topAnchor, constant: 20).isActive = true
playerSeriesTitle.leadingAnchor.constraint(equalTo: playerImage.trailingAnchor, constant: 10).isActive = true
playerSeriesTitle.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 10).isActive = true
playerSeriesTitle.heightAnchor.constraint(equalToConstant: 20).isActive = true
UIView.animate(withDuration: 0.5, animations: {
self.frame.origin.y -= 150
self.playerImage.frame.origin.y -= 150
self.playerTitle.frame.origin.y -= 150
self.playerSeriesTitle.frame.origin.y -= 150
}, completion: nil)
self.setNeedsLayout()
self.reloadInputViews()
}
}
If you want to show view in each view controller then as per view hierarchy you must have to add in UIWindow. UIWindow is base of all screen.
AppDelegate.shared.window?.addSubview(AudioPlayer)
You can add your view to UIWindow.
I am doing the same thing with below method in AppDelegate.
var window: UIWindow?
func addPlayerViewAtBottom() {
var bottomView : PlayerBottomView!
bottomView = PlayerBottomView(frame: CGRect(x: 0, y: UIScreen.main.bounds.size.height - 60, width: UIScreen.main.bounds.width, height: 60))
self.window?.addSubview(bottomView)
self.window?.bringSubview(toFront: bottomView)
}