Applying a shadow to a subview loaded from nib - ios

I am loading a custom view class from the nib at viewWillAppear. After loading the nib, I add it as a subview, then apply some constraints, then update the constraints. At that point I attempt to add a shadow to this view, but no shadow is displaying....
Just for some context, the ViewController has scrollview that contains a contentView. All of the view's content goes inside this contentView.
Here is my code:
ViewController
class MyViewController: UIViewController {
#IBOutlet weak var contentView: UIView!
var matches: [Match] = []
var matchViews: [MatchDetailsView] = []
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setMatches()
}
func setMatches() {
// Match One
let matchOneDetailsView = MatchDetailsView.instanceFromNib(match: matches.count > 0 ? matches[0] : nil, delegate: self, tag: 1)
contentView.addSubview(matchOneDetailsView)
matchOneDetailsView.translatesAutoresizingMaskIntoConstraints = false
let matchOneLeadingConstraint = NSLayoutConstraint(item: matchOneDetailsView, attribute: .leading, relatedBy: .equal, toItem: contentView, attribute: .leading, multiplier: 1, constant: 16)
let matchOneTrailingConstraint = NSLayoutConstraint(item: matchOneDetailsView, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: -16)
let matchOneTopConstraint = NSLayoutConstraint(item: matchOneDetailsView, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: 32)
// Match Two
let matchTwoDetailsView = MatchDetailsView.instanceFromNib(match: matches.count > 1 ? matches[1] : nil, delegate: self, tag: 2)
contentView.addSubview(matchTwoDetailsView)
matchTwoDetailsView.translatesAutoresizingMaskIntoConstraints = false
let matchTwoLeadingConstraint = NSLayoutConstraint(item: matchTwoDetailsView, attribute: .leading, relatedBy: .equal, toItem: contentView, attribute: .leading, multiplier: 1, constant: 16)
let matchTwoTrailingConstraint = NSLayoutConstraint(item: matchTwoDetailsView, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: -16)
let matchTwoTopConstraint = NSLayoutConstraint(item: matchTwoDetailsView, attribute: .top, relatedBy: .equal, toItem: matchOneDetailsView, attribute: .bottom, multiplier: 1, constant: 32)
// Match Three
let matchThreeDetailsView = MatchDetailsView.instanceFromNib(match: matches.count > 2 ? matches[2] : nil, delegate: self, tag: 3)
contentView.addSubview(matchThreeDetailsView)
matchThreeDetailsView.translatesAutoresizingMaskIntoConstraints = false
let matchThreeLeadingConstraint = NSLayoutConstraint(item: matchThreeDetailsView, attribute: .leading, relatedBy: .equal, toItem: contentView, attribute: .leading, multiplier: 1, constant: 16)
let matchThreeTrailingConstraint = NSLayoutConstraint(item: matchThreeDetailsView, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: -16)
let matchThreeTopConstraint = NSLayoutConstraint(item: matchThreeDetailsView, attribute: .top, relatedBy: .equal, toItem: matchTwoDetailsView, attribute: .bottom, multiplier: 1, constant: 32)
let matchThreeBottomConstraint = NSLayoutConstraint(item: matchThreeDetailsView, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: -32)
NSLayoutConstraint.activate([matchOneLeadingConstraint, matchOneTrailingConstraint, matchOneTopConstraint, matchTwoLeadingConstraint, matchTwoTrailingConstraint, matchTwoTopConstraint, matchThreeLeadingConstraint, matchThreeTrailingConstraint, matchThreeTopConstraint, matchThreeBottomConstraint])
updateViewConstraints()
matchViews.append(matchOneDetailsView)
matchViews.append(matchTwoDetailsView)
matchViews.append(matchThreeDetailsView)
styleMatchViews()
}
func styleMatchViews() {
for view in matchViews {
view.addShadow(opacity: 0.25, yOffset: 0, xOffset: 0, radius: 5.0)
view.roundCorners(withRadius: 5.0)
}
}
}
MatchDetailsView
class MatchDetailsView: UIView {
class func instanceFromNib(match: Match?, delegate: MatchDetailsViewDelegate, tag: Int) -> MatchDetailsView {
let view = UINib(nibName: "MatchDetailsView", bundle: nil).instantiate(withOwner: MatchDetailsView(), options: nil)[0] as! MatchDetailsView
view.translatesAutoresizingMaskIntoConstraints = false
view.updateMatchContent()
return view
}
}
UIViewExtension
extension UIView {
/**
Add Shadow
Puts a Drop Shadow in the UIView
*/
func addShadow(opacity: Float, yOffset: Int, xOffset: Int, radius: CGFloat) {
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = opacity
self.layer.shadowOffset = CGSize(width: xOffset, height: yOffset)
self.layer.shadowRadius = radius
self.layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
}
}
If MatchDetailsView's nib file has 'clipsToBounds' set to false, why isn't my shadow showing up when the page is loaded? Am I missing a layout function even though they seem to be laid out correctly?

You are setting the .shadowPath to the bounds of the view... but the bounds at that point may not be (probably will not be) the bounds after auto-layout lays out the views.
For a "shadowed view" you are much better off setting the shadow inlayoutSubviews() inside the view itself.
You should also add and setup your subviews in viewDidLoad() viewWillAppear() may be called more than once (depending on your navigation), and you'll end up adding multiple copies of your subviews.
So...
Change your MatchDetailsView class like this:
class MatchDetailsView: UIView {
var opacity: CGFloat = 0.0
var xOffset: CGFloat = 0.0
var yOffset: CGFloat = 0.0
// for the shadowRadius
var sRadius: CGFloat = 0.0
// for the cornerRadius
var cRadius: CGFloat = 0.0
override func layoutSubviews() {
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = Float(opacity)
self.layer.shadowOffset = CGSize(width: xOffset, height: yOffset)
self.layer.shadowRadius = sRadius
self.layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
self.layer.cornerRadius = cRadius
}
class func instanceFromNib(match: Match?, delegate: MatchDetailsViewDelegate, tag: Int) -> MatchDetailsView {
let view = UINib(nibName: "MatchDetailsView", bundle: nil).instantiate(withOwner: MatchDetailsView(), options: nil)[0] as! MatchDetailsView
view.translatesAutoresizingMaskIntoConstraints = false
view.updateMatchContent()
return view
}
}
Then, in your view controller:
override func viewDidLoad() {
super.viewDidLoad()
setMatches()
}
func setMatches() {
// Match One
let matchOneDetailsView = MatchDetailsView.instanceFromNib(match: matches.count > 0 ? matches[0] : nil, delegate: self, tag: 1)
contentView.addSubview(matchOneDetailsView)
matchOneDetailsView.translatesAutoresizingMaskIntoConstraints = false
let matchOneLeadingConstraint = NSLayoutConstraint(item: matchOneDetailsView, attribute: .leading, relatedBy: .equal, toItem: contentView, attribute: .leading, multiplier: 1, constant: 16)
let matchOneTrailingConstraint = NSLayoutConstraint(item: matchOneDetailsView, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: -16)
let matchOneTopConstraint = NSLayoutConstraint(item: matchOneDetailsView, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: 32)
// Match Two
let matchTwoDetailsView = MatchDetailsView.instanceFromNib(match: matches.count > 1 ? matches[1] : nil, delegate: self, tag: 2)
contentView.addSubview(matchTwoDetailsView)
matchTwoDetailsView.translatesAutoresizingMaskIntoConstraints = false
let matchTwoLeadingConstraint = NSLayoutConstraint(item: matchTwoDetailsView, attribute: .leading, relatedBy: .equal, toItem: contentView, attribute: .leading, multiplier: 1, constant: 16)
let matchTwoTrailingConstraint = NSLayoutConstraint(item: matchTwoDetailsView, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: -16)
let matchTwoTopConstraint = NSLayoutConstraint(item: matchTwoDetailsView, attribute: .top, relatedBy: .equal, toItem: matchOneDetailsView, attribute: .bottom, multiplier: 1, constant: 32)
// Match Three
let matchThreeDetailsView = MatchDetailsView.instanceFromNib(match: matches.count > 2 ? matches[2] : nil, delegate: self, tag: 3)
contentView.addSubview(matchThreeDetailsView)
matchThreeDetailsView.translatesAutoresizingMaskIntoConstraints = false
let matchThreeLeadingConstraint = NSLayoutConstraint(item: matchThreeDetailsView, attribute: .leading, relatedBy: .equal, toItem: contentView, attribute: .leading, multiplier: 1, constant: 16)
let matchThreeTrailingConstraint = NSLayoutConstraint(item: matchThreeDetailsView, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: -16)
let matchThreeTopConstraint = NSLayoutConstraint(item: matchThreeDetailsView, attribute: .top, relatedBy: .equal, toItem: matchTwoDetailsView, attribute: .bottom, multiplier: 1, constant: 32)
let matchThreeBottomConstraint = NSLayoutConstraint(item: matchThreeDetailsView, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: -32)
NSLayoutConstraint.activate([matchOneLeadingConstraint, matchOneTrailingConstraint, matchOneTopConstraint, matchTwoLeadingConstraint, matchTwoTrailingConstraint, matchTwoTopConstraint, matchThreeLeadingConstraint, matchThreeTrailingConstraint, matchThreeTopConstraint, matchThreeBottomConstraint])
matchViews.append(matchOneDetailsView)
matchViews.append(matchTwoDetailsView)
matchViews.append(matchThreeDetailsView)
styleMatchViews()
}
func styleMatchViews() {
for view in matchViews {
view.opacity = 0.25
view.yOffset = 0.0
view.xOffset = 0.0
view.sRadius = 5.0
view.cRadius = 5.0
}
}

Related

Programmatically made constraints are not working

As I want to move away from xib and make my layout programmatically, I found that using the same exact constraints doesn't work as I would expect.
I want to make this UITableViewCell
It's a quite simple cell with a small icon to its right as well as an Activity Indicator so I can toggle which one I want to see. They are inside a View and to their left is a label
Those are my constraints in the outline view
And it works perfectly. However when I'm removing the XIB and doing all of the code myself, nothing works anymore
So here's my code:
class StandardRow: UITableViewCell {
private var initialWidth: CGFloat = 20
public var fetching: Bool = false {
didSet {
if (fetching) {
activityIndicator?.startAnimating()
} else {
activityIndicator?.stopAnimating()
}
changeImageWidth()
}
}
public var rightImage: UIImage? = nil {
didSet {
rightImageView?.image = rightImage
changeImageWidth()
}
}
private func changeImageWidth() {
if (activityIndicator?.isAnimating) ?? false || rightImage != nil {
imageWidth?.constant = initialWidth
} else {
imageWidth?.constant = 0
}
}
override func prepareForReuse() {
valueLabel?.text = ""
imageView?.image = nil
rightImage = nil
fetching = false
textLabel?.text = ""
accessoryType = .none
}
//Views
private var imageContainer = UIView()
private var rightImageView = UIImageView()
private var activityIndicator: UIActivityIndicatorView? = UIActivityIndicatorView()
public var valueLabel: UILabel? = UILabel()
private var imageWidth: NSLayoutConstraint? = nil
override init(style: UITableViewCell.CellStyle = .default, reuseIdentifier: String? = nil) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
buildView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
buildView()
}
func buildView() {
contentView.addSubview(valueLabel!)
imageContainer.addSubview(rightImageView)
imageContainer.addSubview(activityIndicator!)
contentView.addSubview(imageContainer)
imageContainer.backgroundColor = .red
}
override func layoutSubviews() {
super.layoutSubviews()
//IMAGE CONTAINER CONSTRAINTS
imageWidth = NSLayoutConstraint(item: imageContainer, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: initialWidth)
imageWidth?.priority = UILayoutPriority(rawValue: 999)
imageWidth?.isActive = true
let bottomImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: 0)
bottomImageContainerConstraint.isActive = true
bottomImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
let topImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: 0)
topImageContainerConstraint.isActive = true
topImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
let trailingImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: 5)
trailingImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
trailingImageContainerConstraint.isActive = true
let centerYImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)
centerYImageContainerConstraint.isActive = true
centerYImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
//VALUE LABEL CONSTRAINTS
let trailingValueLabelConstraint = NSLayoutConstraint(item: valueLabel!, attribute: .trailing, relatedBy: .equal, toItem: imageContainer, attribute: .leading, multiplier: 1, constant: 5)
trailingValueLabelConstraint.isActive = true
trailingValueLabelConstraint.priority = UILayoutPriority(rawValue: 999)
let centerYValueLabelConstraint = NSLayoutConstraint(item: valueLabel!, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)
centerYValueLabelConstraint.isActive = true
centerYValueLabelConstraint.priority = UILayoutPriority(rawValue: 999)
//ACTIVITY INDICATOR CONSTRAINGS
NSLayoutConstraint(item: activityIndicator!, attribute: .trailing, relatedBy: .equal, toItem: imageContainer, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: activityIndicator!, attribute: .leading, relatedBy: .equal, toItem: imageContainer, attribute: .leading, multiplier: 1, constant: 11).isActive = false
NSLayoutConstraint(item: activityIndicator!, attribute: .bottom, relatedBy: .equal, toItem: imageContainer, attribute: .bottom, multiplier: 1, constant: 11).isActive = false
NSLayoutConstraint(item: activityIndicator!, attribute: .top, relatedBy: .equal, toItem: imageContainer, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: activityIndicator!, attribute: .centerY, relatedBy: .equal, toItem: imageContainer, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
//RIGHT IMAGE VIEW CONSTRAINTS
NSLayoutConstraint(item: rightImageView, attribute: .trailing, relatedBy: .equal, toItem: activityIndicator!, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .leading, relatedBy: .equal, toItem: rightImageView, attribute: .leading, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .bottom, relatedBy: .equal, toItem: activityIndicator!, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .top, relatedBy: .equal, toItem: activityIndicator!, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator!, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
//changeImageWidth()
}}
So I have a few ideas to where it can come from, firstly being "translatesAutoresizingMaskIntoConstraints" set to true by default, but when I'm setting it to false in the superview then my cell doesn't show anymore and in the contentView, Xcode tells me I shouldn't do that because of an undefined behaviour
I'm also using Reveal to debug my UI and then I found those peculiar values:
Which is not what I want, Reveal is reporting that those constraints are translating the autoresizing mask of the view to autolayout so it would confirm the previous theory. I did set the priority to 999 to some of the constraints because otherwise they would be broken.
I'm actually at a dead end and I think I'm missing something but I can't pinpoint what as I don't have enough experience with non-interface builder constraints
Try Anchors, it's much easier.
Example
var redView = UIView()
redView.backgroundColor = .red
anyView.addsubView(redView)
redView.translatesAutoresizingMaskIntoConstraints = false
redView.centerXAnchor.constraint(equalTo: self.parentView.centerXAnchor).isActive = true
redView.centerYAnchor.constraint(equalTo: self.parentView.centerYAnchor).isActive = true
redView.heightAnchor.constraint(equalToConstant: 100).isActive = true
redView.widthAnchor.constraint(equalToConstant: 100).isActive = true
You can add the same method to your UIView extension
func constrainToEdges(_ subview: UIView, top: CGFloat = 0, bottom: CGFloat = 0, leading: CGFloat = 0, trailing: CGFloat = 0) {
subview.translatesAutoresizingMaskIntoConstraints = false
let topContraint = NSLayoutConstraint(
item: subview,
attribute: .top,
relatedBy: .equal,
toItem: self,
attribute: .top,
multiplier: 1.0,
constant: top)
let bottomConstraint = NSLayoutConstraint(
item: subview,
attribute: .bottom,
relatedBy: .equal,
toItem: self,
attribute: .bottom,
multiplier: 1.0,
constant: bottom)
let leadingContraint = NSLayoutConstraint(
item: subview,
attribute: .leading,
relatedBy: .equal,
toItem: self,
attribute: .leading,
multiplier: 1.0,
constant: leading)
let trailingContraint = NSLayoutConstraint(
item: subview,
attribute: .trailing,
relatedBy: .equal,
toItem: self,
attribute: .trailing,
multiplier: 1.0,
constant: trailing)
addConstraints([
topContraint,
bottomConstraint,
leadingContraint,
trailingContraint])
}
I recommend using this framework for building constraint based layouts programmatically, it makes the process straightforward and faster. Take the setup for the contentView of this cell for example:
contentView.addSubview(descriptionLabel)
contentView.addSubview(amountLabel)
contentView.addSubview(dateLabel)
contentView.addSubview(bottomRightLabel)
constrain(descriptionLabel, amountLabel, dateLabel, bottomRightLabel) { desc, amount, date, bottomRight in
desc.top == desc.superview!.top + 16
desc.left == desc.superview!.left + 16
desc.right <= amount.left + 12
desc.bottom == date.top - 12
amount.centerY == desc.centerY
amount.right == amount.superview!.right - 12
date.left == date.superview!.left + 16
date.right <= bottomRight.left - 12
date.bottom == date.superview!.bottom - 16
bottomRight.centerY == date.centerY
bottomRight.right == bottomRight.superview!.right - 12
}

UIView appearing from left and disappearing from right Animation

I have a UITableView with custom sections programatically done. I am aiming to have a button in the navigation bar that will trigger a functions that will show or will hide a UIView with an appearing from left and disappearing from right Animation. So far:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
/** Header View **/
let headerView = UITableViewHeaderFooterView()
headerView.backgroundColor = UIColor.VinylRecorderTheme.white
headerView.addBottomBorder(color: UIColor.init(red: 250/255, green: 250/255, blue: 250/255, alpha: 1), height: 1, margins: 0)
headerView.tag = section
headerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerViewTouched)))
/* Header ImageView */
let headerImageView = UIImageView()
if let imageForHeader = vinylRecorderHelper.loadThimbnailFromMetadata(url: iCloudAlbumArray[section].urlTrackArray[0].url){
headerImageView.image = imageForHeader
}
else{
headerImageView.image = UIImage(named: "music_album_ic")
}
headerView.addSubview(headerImageView)
headerImageView.translatesAutoresizingMaskIntoConstraints = false
headerView.addConstraint(NSLayoutConstraint(item: headerImageView, attribute: .leading, relatedBy: .equal, toItem: headerView, attribute: .leading, multiplier: 1, constant: 8))
headerView.addConstraint(NSLayoutConstraint(item: headerImageView, attribute: .centerY, relatedBy: .equal, toItem: headerView, attribute: .centerY, multiplier: 1, constant: 0))
headerView.addConstraint(NSLayoutConstraint(item: headerImageView, attribute: .height, relatedBy: .equal, toItem: headerView, attribute: .height, multiplier: 0.65, constant: 0))
headerView.addConstraint(NSLayoutConstraint(item: headerImageView, attribute: .width, relatedBy: .equal, toItem: headerImageView, attribute: .height, multiplier: 1, constant: 0))
/** Header Button **/
let buttonWidth = currentDeviceTraitStatus == .wreghreg ? CGFloat(34):CGFloat(16)
let headerExpandButton = UIButton()
headerExpandButton.translatesAutoresizingMaskIntoConstraints = false
headerExpandButton.contentMode = .scaleAspectFit
headerExpandButton.setImage(#imageLiteral(resourceName: "arrow_rigth_ic"), for: .normal)
headerExpandButton.setImage(#imageLiteral(resourceName: "arrow_down_ic"), for: .selected)
headerExpandButton.tag = expandButtonTag
headerView.addSubview(headerExpandButton)
headerView.addConstraint(NSLayoutConstraint(item: headerExpandButton, attribute: .trailing, relatedBy: .equal, toItem: headerView, attribute: .trailing, multiplier: 1, constant: -8))
headerView.addConstraint(NSLayoutConstraint(item: headerExpandButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: buttonWidth))
headerView.addConstraint(NSLayoutConstraint(item: headerExpandButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: buttonWidth))
headerView.addConstraint(NSLayoutConstraint(item: headerExpandButton, attribute: .centerY, relatedBy: .equal, toItem: headerView, attribute: .centerY, multiplier: 1, constant: 0))
/** Header Label **/
let headerNameLabel = UILabel()
headerNameLabel.text = "\(iCloudAlbumArray[section].artistName) - \(iCloudAlbumArray[section].albumName)"
headerNameLabel.font = currentDeviceTraitStatus == .wreghreg ? .boldSystemFont(ofSize: 18):.boldSystemFont(ofSize: 15)
headerView.addSubview(headerNameLabel)
headerNameLabel.translatesAutoresizingMaskIntoConstraints = false
headerView.addConstraint(NSLayoutConstraint(item: headerNameLabel, attribute: .left, relatedBy: .equal, toItem: headerImageView, attribute: .right, multiplier: 1, constant: 8))
headerView.addConstraint(NSLayoutConstraint(item: headerNameLabel, attribute: .top, relatedBy: .equal, toItem: headerView, attribute: .top, multiplier: 1, constant: 0))
headerView.addConstraint(NSLayoutConstraint(item: headerNameLabel, attribute: .bottom, relatedBy: .equal, toItem: headerView, attribute: .bottom, multiplier: 1, constant: 0))
headerView.addConstraint(NSLayoutConstraint(item: headerExpandButton, attribute: .leading, relatedBy: .equal, toItem: headerNameLabel, attribute: .trailing, multiplier: 1, constant: 8))
/** Remove Button Label **/
let removeButton = UIButton()
removeButton.backgroundColor = UIColor.VinylRecorderTheme.redApple
removeButton.tag = deleteButtonTag
headerView.addSubview(removeButton)
removeButton.translatesAutoresizingMaskIntoConstraints = false
headerView.addConstraint(NSLayoutConstraint(item: removeButton, attribute: .leading, relatedBy: .equal, toItem: headerView, attribute: .leading, multiplier: 1, constant: 0))
headerView.addConstraint(NSLayoutConstraint(item: removeButton, attribute: .top, relatedBy: .equal, toItem: headerView, attribute: .top, multiplier: 1, constant: 0))
headerView.addConstraint(NSLayoutConstraint(item: removeButton, attribute: .bottom, relatedBy: .equal, toItem: headerView, attribute: .bottom, multiplier: 1, constant: 0))
removeButton.widthAnchor.constraint(equalToConstant: 0).isActive = true
removeButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(removeViewTouched)))
return headerView
}
And the function that will perform the desired Animation is:
#IBAction func startStopEditAction(_ sender: UIButton) {
print("\(logClassName): startStopEditAction.")
sender.isSelected = !sender.isSelected
if let sectionView = iCloudExportsTableView.headerView(forSection: 0){
print("\(logClassName): startStopEditAction in header")
let removeButton:UIButton = sectionView.viewWithTag(deleteButtonTag) as! UIButton
//removeButton.isHidden = !removeButton.isHidden
//removeButton.widthAnchor.constraint(equalToConstant: isEditingRemove ? 60 : 0)
UIView.transition(with: view, duration: 1.5, options: .transitionCrossDissolve, animations: {
removeButton.widthAnchor.constraint(equalToConstant: 0).isActive = false
removeButton.widthAnchor.constraint(equalToConstant: 60).isActive = true
//removeButton.isHidden = !removeButton.isHidden
})
}
}
EDIT
The UIView (Button)is
let removeButton = UIButton()
After some work around I have been able to achieve what I was looking for.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
/** Header View **/
let headerView = UITableViewHeaderFooterView()
headerView.backgroundColor = UIColor.VinylRecorderTheme.white
headerView.addBottomBorder(color: UIColor.init(red: 250/255, green: 250/255, blue: 250/255, alpha: 1), height: 1, margins: 0)
headerView.tag = section
headerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerViewTouched)))
**************
/** Remove Button **/
let removeButton = UIButton()
removeButton.backgroundColor = UIColor.VinylRecorderTheme.redApple
removeButton.tag = section
removeButton.setTitle(NSLocalizedString("com.messages.delete", comment: ""), for: .normal)
removeButton.titleLabel?.font = currentDeviceTraitStatus == .wreghreg ? .boldSystemFont(ofSize: 18):.boldSystemFont(ofSize: 15)
removeButton.setTitleColor(UIColor.white, for: .normal)
headerView.addSubview(removeButton)
removeButton.translatesAutoresizingMaskIntoConstraints = false
headerView.addConstraint(NSLayoutConstraint(item: removeButton, attribute: .leading, relatedBy: .equal, toItem: headerView, attribute: .leading, multiplier: 1, constant: 0))
headerView.addConstraint(NSLayoutConstraint(item: removeButton, attribute: .top, relatedBy: .equal, toItem: headerView, attribute: .top, multiplier: 1, constant: 0))
headerView.addConstraint(NSLayoutConstraint(item: removeButton, attribute: .bottom, relatedBy: .equal, toItem: headerView, attribute: .bottom, multiplier: 1, constant: 0))
let widthConstraint = NSLayoutConstraint(item: removeButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: self.isEditingRemove ? 90:0)
widthConstraint.identifier = tableViewSectionWidthId
headerView.addConstraint(widthConstraint)
removeButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(removeViewTouched)))
return headerView
}
And then In the IBAction:
#IBAction func startStopEditAction(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
let numberOfSections = iCloudExportsTableView.numberOfSections
for i in 0..<numberOfSections{
if let sectionView = iCloudExportsTableView.headerView(forSection: i){
let filteredConstraints = sectionView.constraints.filter { $0.identifier == tableViewSectionWidthId }
if let widthConstraint = filteredConstraints.first {
widthConstraint.constant = self.isEditingRemove ? 90:0
UIView.animate(withDuration: 0.5, delay: TimeInterval.init(Double(i) * Double(0.20)), options: UIViewAnimationOptions.curveEaseInOut, animations: {
sectionView.layoutIfNeeded()
}, completion: { (finished) in
})
}
}
}
}

How to resolve : Unable to simultaneously satisfy constraints

I want to create like this ( Done through storyboard )
This Done through storyboard. In my observation I found the constrain problem arise from "totalAmountImg". I don't change the priority property of all objects (all have priority == 1000)
This is the result. (SummaryVC - Done this programatically)
NOTE : I want to make the SummaryVC totalAmountImg ( Programatically ) look like above image (Made by storyboard)
Stroryboard constraints for totalAmountImg
This exactly I create through code. But I don't know why this error pop out.
totalImgConstraint
func totalAmountImgConstraints() {
let imgCenterY = NSLayoutConstraint(item: totalAmountImg,
attribute: .centerY,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .centerY,
multiplier: 1.0,
constant: 0)
imgCenterY.identifier = "imgCenterY"
imgCenterY.isActive = true
let imgRight = NSLayoutConstraint(item: totalAmountImg,
attribute: .right,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .right,
multiplier: 1.0,
constant: 16)
imgRight.identifier = "imgRight"
imgRight.isActive = true
let imgAspectRatio = NSLayoutConstraint(item: totalAmountImg,
attribute: .height,
relatedBy: .equal,
toItem: totalAmountImg,
attribute: .width,
multiplier: 1.0 / 1.0,
constant: 0)
imgAspectRatio.identifier = "imgAspectRatio"
imgAspectRatio.isActive = true
let imgLeft = NSLayoutConstraint(item: totalAmountImg,
attribute: .left,
relatedBy: .equal,
toItem: totalPriceLbl,
attribute: .right,
multiplier: 1.0,
constant: 4)
imgLeft.identifier = "imgLeft"
imgLeft.isActive = true
let imgWidth = NSLayoutConstraint(item: totalAmountImg,
attribute: .width,
relatedBy: .lessThanOrEqual,
toItem: totalAmountView,
attribute: .width,
multiplier: 0.2,
constant: 0)
imgWidth.identifier = "imgWidth"
imgWidth.isActive = true
}
SummaryVC
class SummaryVC: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageController: UIPageControl!
let numberOfPages = 3
override func viewDidLoad() {
super.viewDidLoad()
basicDesignSetup()
// Add subViews
scrollView.addSubview(summaryBaseView)
scrollView.addSubview(paymentTypeBaseView)
scrollView.addSubview(itemNameBaseView)
summaryBaseView.addSubview(totalAmountView)
summaryBaseView.addSubview(runningTableView)
summaryBaseView.addSubview(partnerAmountView)
totalAmountView.addSubview(totalLbl)
totalAmountView.addSubview(totalPriceLbl)
totalAmountView.addSubview(totalAmountImg)
view.setNeedsUpdateConstraints()
// set content size
scrollView.contentSize = CGSize(width: scrollView.frame.width * CGFloat(numberOfPages), height: scrollView.frame.height)
}
override func viewDidLayoutSubviews() {
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = scrollView.contentOffset.x / scrollView.frame.width
pageController.currentPage = Int(pageNumber)
pageController.currentPageIndicatorTintColor = UIColor.white
}
//-----------------------------------------------------------------
// MARK: - Methods
//-----------------------------------------------------------------
func basicDesignSetup() {
pageController.numberOfPages = numberOfPages
let scrollViewSize = scrollView.frame.size
let scrollViewWidth = scrollView.frame.width
// Summary View
let summeryOrigin = CGPoint(x: 0, y: 0)
summaryBaseView = UIView(frame: CGRect(origin: summeryOrigin, size: scrollViewSize))
summaryBaseView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
// Payment Type View
let paymentTypeOrigin = CGPoint(x: scrollViewWidth, y: 0)
paymentTypeBaseView = UIView(frame: CGRect(origin: paymentTypeOrigin, size: scrollViewSize))
paymentTypeBaseView.backgroundColor = #colorLiteral(red: 0.01680417731, green: 0.1983509958, blue: 1, alpha: 1)
// Item Name View
let itemNameOrigin = CGPoint(x: scrollViewWidth * 2, y: 0)
itemNameBaseView = UIView(frame: CGRect(origin: itemNameOrigin, size: scrollViewSize))
itemNameBaseView.backgroundColor = #colorLiteral(red: 0, green: 0.9914394021, blue: 1, alpha: 1)
// Total Amount View
totalAmountView.backgroundColor = #colorLiteral(red: 0.5480614305, green: 0.8129847646, blue: 0.6160266995, alpha: 1)
runningTableView.backgroundColor = #colorLiteral(red: 0.4280827343, green: 0.7700845003, blue: 0.9571052194, alpha: 1)
partnerAmountView.backgroundColor = #colorLiteral(red: 0.9137254902, green: 0.4470588235, blue: 0.4549019608, alpha: 1)
totalLbl.text = "Total Amount"
totalLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
totalLbl.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
totalLbl.font = UIFont.systemFont(ofSize: 16, weight: .medium)
totalPriceLbl.text = "5532.00"
totalPriceLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
totalPriceLbl.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
totalAmountImg.contentMode = .scaleAspectFit
totalAmountImg.image = #imageLiteral(resourceName: "tick")
totalAmountImg.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
}
//-----------------------------------------------------------------
// MARK: - Views - programatically
//-----------------------------------------------------------------
// Base Views
lazy var summaryBaseView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var paymentTypeBaseView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var itemNameBaseView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// $$$$$ Summary Sub Views $$$$$
// Total Amount
lazy var totalAmountView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var totalLbl: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
lazy var totalPriceLbl: UnderlinedLabel = {
let label = UnderlinedLabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
lazy var totalAmountImg: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
// Running Table
lazy var runningTableView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var runningLbl: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
lazy var runningPriceLbl: UnderlinedLabel = {
let label = UnderlinedLabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
lazy var runningTableImg: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
// Partner Amount
lazy var partnerAmountView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var partnerLbl: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
lazy var partnerPriceLbl: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
lazy var partnerAmountImg: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
//-----------------------------------------------------------------
// MARK: - Constraints
//-----------------------------------------------------------------
override func updateViewConstraints() {
totalAmountViewConstraints()
runningTableViewConstraints()
partnerAmountViewConstraints()
// Total Amount View
totalLblConstraint()
totalPriceLblConstraints()
totalAmountImgConstraints()
super.updateViewConstraints()
}
func totalAmountViewConstraints() {
NSLayoutConstraint(item: totalAmountView,
attribute: .left,
relatedBy: .equal,
toItem: summaryBaseView,
attribute: .left,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalAmountView,
attribute: .right,
relatedBy: .equal,
toItem: summaryBaseView,
attribute: .right,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalAmountView,
attribute: .top,
relatedBy: .equal,
toItem: summaryBaseView,
attribute: .top,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalAmountView,
attribute: .bottom,
relatedBy: .equal,
toItem: runningTableView,
attribute: .top,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalAmountView,
attribute: .height,
relatedBy: .equal,
toItem: runningTableView,
attribute: .height,
multiplier: 1.0,
constant: 0).isActive = true
}
func runningTableViewConstraints() {
NSLayoutConstraint(item: runningTableView,
attribute: .leading,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .leading,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: runningTableView,
attribute: .trailing,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .trailing,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: runningTableView,
attribute: .top,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .bottom,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: runningTableView,
attribute: .bottom,
relatedBy: .equal,
toItem: partnerAmountView,
attribute: .top,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: runningTableView,
attribute: .height,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .height,
multiplier: 1.0,
constant: 0).isActive = true
}
func partnerAmountViewConstraints() {
NSLayoutConstraint(item: partnerAmountView,
attribute: .leading,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .leading,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: partnerAmountView,
attribute: .trailing,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .trailing,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: partnerAmountView,
attribute: .top,
relatedBy: .equal,
toItem: runningTableView,
attribute: .bottom,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: partnerAmountView,
attribute: .bottom,
relatedBy: .equal,
toItem: summaryBaseView,
attribute: .bottom,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: partnerAmountView,
attribute: .height,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .height,
multiplier: 1.0,
constant: 0).isActive = true
}
// Total Ammount Section Subviews
func totalLblConstraint() {
NSLayoutConstraint(item: totalLbl,
attribute: .top,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .top,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalLbl,
attribute: .left,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .left,
multiplier: 1.0,
constant: 16).isActive = true
NSLayoutConstraint(item: totalLbl,
attribute: .bottom,
relatedBy: .equal,
toItem: totalPriceLbl,
attribute: .top,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalLbl,
attribute: .height,
relatedBy: .equal,
toItem: totalPriceLbl,
attribute: .height,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalLbl,
attribute: .width,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .width,
multiplier: 0.7,
constant: 0).isActive = true
}
func totalPriceLblConstraints() {
NSLayoutConstraint(item: totalPriceLbl,
attribute: .top,
relatedBy: .equal,
toItem: totalLbl,
attribute: .bottom,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalPriceLbl,
attribute: .leading,
relatedBy: .equal,
toItem: totalLbl,
attribute: .leading,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalPriceLbl,
attribute: .bottom,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .bottom,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalPriceLbl,
attribute: .height,
relatedBy: .equal,
toItem: totalLbl,
attribute: .height,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalPriceLbl,
attribute: .width,
relatedBy: .equal,
toItem: totalLbl,
attribute: .width,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: totalPriceLbl,
attribute: .trailing,
relatedBy: .equal,
toItem: totalLbl,
attribute: .trailing,
multiplier: 1.0,
constant: 0).isActive = true
}
func totalAmountImgConstraints() {
let imgCenterY = NSLayoutConstraint(item: totalAmountImg,
attribute: .centerY,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .centerY,
multiplier: 1.0,
constant: 0)
imgCenterY.identifier = "imgCenterY"
imgCenterY.isActive = true
let imgRight = NSLayoutConstraint(item: totalAmountImg,
attribute: .right,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .right,
multiplier: 1.0,
constant: 16)
imgRight.identifier = "imgRight"
imgRight.isActive = true
let imgAspectRatio = NSLayoutConstraint(item: totalAmountImg,
attribute: .height,
relatedBy: .equal,
toItem: totalAmountImg,
attribute: .width,
multiplier: 1.0 / 1.0,
constant: 0)
imgAspectRatio.identifier = "imgAspectRatio"
imgAspectRatio.isActive = true
let imgLeft = NSLayoutConstraint(item: totalAmountImg,
attribute: .left,
relatedBy: .equal,
toItem: totalPriceLbl,
attribute: .right,
multiplier: 1.0,
constant: 4)
imgLeft.identifier = "imgLeft"
imgLeft.isActive = true
let imgWidth = NSLayoutConstraint(item: totalAmountImg,
attribute: .width,
relatedBy: .lessThanOrEqual,
toItem: totalAmountView,
attribute: .width,
multiplier: 0.2,
constant: 0)
imgWidth.identifier = "imgWidth"
imgWidth.isActive = true
}
//-----------------------------------------------------------------
// MARK: - Actions
//-----------------------------------------------------------------
#IBAction func pageChange(_ sender: UIPageControl) {
let x = CGFloat(sender.currentPage) * scrollView.frame.width
scrollView.contentOffset = CGPoint(x: x, y: 0)
pageController.currentPageIndicatorTintColor = UIColor.white
}
}
extension NSLayoutConstraint {
override open var description: String {
let id = identifier ?? ""
return "id: \(id), constant: \(constant)" //you may print whatever you want here
}
}
LOG
2018-01-12 16:27:27.934408+0530 Cafe Point[13577:258401] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"id: , constant: 343.0",
"id: , constant: 0.0",
"id: , constant: 0.0",
"id: , constant: 16.0",
"id: , constant: 0.0",
"id: , constant: 0.0",
"id: , constant: 0.0",
"id: imgLeft, constant: 4.0",
"id: imgRight, constant: 16.0",
"id: imgWidth, constant: 0.0"
)
Will attempt to recover by breaking constraint
id: imgLeft, constant: 4.0
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Try changing to this:
let imgRight = NSLayoutConstraint(item: totalAmountImg,
attribute: .right,
relatedBy: .equal,
toItem: totalAmountView,
attribute: .right,
multiplier: 1.0,
constant: -16)
imgRight.identifier = "imgRight"
imgRight.isActive = true
(note the 16 changes to -16)
You have to be careful when comparing constraints from the storyboard to ones created programatically because the order matters.
As the Warning states:
Will attempt to recover by breaking constraint id: imgLeft, constant:
4.0
You are giving Left and Right constraint to Image View. But also assigning Width. It confuses the Image View since Left and Right Constraints are enough to satisfy Width requirement for the Image View.
Hence you can use two combinations: left/width or right/width.
You can use other combinations too but for that, we have to dig into Priority of constraints.
To correctly understand layout keep in mind when you give a view leading/left and trailing/right constraints then you don't have to give it a width , as if you're pinning a robe to two sides , if you want to give it a width then give it a centerX constraint , Same also applied to top,bottom then don't have to give height and if you want to give height then add centerY constraint , off course you can give a view leading , trailing and width constraints at the same time but to make sure they fit together

How to use a custom UIView multiple times in a controller with different content?

I have created my own custom UIView for a popover that I have to display on a screen. My UIView looks like this:
class HelpTipsPopover: UIView {
weak var title: UILabel!
weak var myText: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
let strongTitle = UILabel()
title = strongTitle
let strongMyText = UILabel()
myText = strongMyText
self.addSubview(strongTitle)
title.translatesAutoresizingMaskIntoConstraints = false
if selected == true{
title.text = "Search"
title.font = UIFont(name: "HelveticaNeue-Bold", size: 12)
title.textColor = UIColor.TRLMBlueBlackColor()
let leftConstraint = NSLayoutConstraint(item: title, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1.0, constant: 10)
let topConstraint = NSLayoutConstraint(item: title, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1.0, constant: 10)
self.addConstraints([leftConstraint, topConstraint])
self.addSubview(strongMyText)
myText.translatesAutoresizingMaskIntoConstraints = false
myText.text = "Search equities to view the order book and market prints for specific moments in time."
myText.numberOfLines = 0
myText.lineBreakMode = NSLineBreakMode.ByWordWrapping
myText.font = UIFont(name: "Helvetica Neue", size: 12)
myText.textColor = UIColor.TRLMBlueBlackColor()
let leftDescription = NSLayoutConstraint(item: myText, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1.0, constant: 10)
let rightDescription = NSLayoutConstraint(item: myText, attribute: .Trailing, relatedBy: .Equal, toItem: self, attribute: .Trailing, multiplier: 1.0, constant: 10)
let topDescription = NSLayoutConstraint(item: myText, attribute: .Top, relatedBy: .Equal, toItem: title, attribute: .Bottom, multiplier: 1.0, constant: 5)
self.addConstraints([leftDescription, topDescription, rightDescription])
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
}
Now I have three popovers that I have to display in my view controller with different title and text in each popover. Here are the methods in my View Controller which displays those popovers:
func showPopover(){
self.view.addSubview(helpTipsPopover)
helpTipsPopover.tag = 1
helpTipsPopover.translatesAutoresizingMaskIntoConstraints = false
helpTipsPopover.layer.cornerRadius = 6
helpTipsPopover.backgroundColor = UIColor(white: 1.0, alpha: 0.8)
let leftConstraint = NSLayoutConstraint(item: helpTipsPopover, attribute: .Leading, relatedBy: .Equal, toItem: self.view, attribute: .Leading, multiplier: 1.0, constant: 10)
let topConstraint = NSLayoutConstraint(item: helpTipsPopover, attribute: .Top, relatedBy: .Equal, toItem: self.hotSpotOne, attribute: .Bottom, multiplier: 1.0, constant: 4)
let widthConstraint = NSLayoutConstraint(item: helpTipsPopover, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 200)
let heightConstraint = NSLayoutConstraint(item: self.helpTipsPopover, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 80)
self.view.addConstraints([leftConstraint, topConstraint, widthConstraint, heightConstraint])
}
func showPopoverTwo(){
self.view.addSubview(helpTipsPopover)
helpTipsPopover.tag = 1
helpTipsPopover.translatesAutoresizingMaskIntoConstraints = false
helpTipsPopover.layer.cornerRadius = 6
helpTipsPopover.backgroundColor = UIColor(white: 1.0, alpha: 0.8)
let centerConstraint = NSLayoutConstraint(item: helpTipsPopover, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1.0, constant: 0)
let topConstraint = NSLayoutConstraint(item: helpTipsPopover, attribute: .Top, relatedBy: .Equal, toItem: self.hotSpotTwo, attribute: .Bottom, multiplier: 1.0, constant: 4)
let widthConstraint = NSLayoutConstraint(item: helpTipsPopover, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 200)
let heightConstraint = NSLayoutConstraint(item: self.helpTipsPopover, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 80)
self.view.addConstraints([centerConstraint, topConstraint, widthConstraint, heightConstraint])
}
Now I want to achieve something like this:
So each popover should have different title and text but i want to reuse the same UIView. Any help is appreciated. Thanks!
Modify your init method
init(frame: CGRect, titleString: String, bodyString: String) {
super.init(frame: frame)
// Your current initializer
title.text = titleString
myText.text = bodyString
}
Then you can initialize a popup like this:
let frame = CGRectMake(0,0,180,100)
let titleString = "My Custom Title"
let bodyString = "This is a body. I'm explaining things to you here."
helpTipsPopoverOne = HelpTipsPopover(frame: frame, titleString: titleString, bodyString: bodyString)
Modify your display method to take a popup as a parameter. Then you can create popups and display with whatever text you'd like!

Auto layout completes animation in much less than the time provided

I want to animate a container width in 10 seconds (for example). I have two views and I assign size constraints to both of them, and call the empty init() method. The problem I'm having - the "animation" is over before you know it, even though I have 10 seconds in for the duration. What gives?
This is a reproducible example of the issue.
class ContainerView: UIView {}
class ChildView: UIView {}
class ViewController: UIViewController {
var containerView: ContainerView!
var childView: ChildView!
var widthConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
containerView = ContainerView()
containerView.backgroundColor = UIColor.blueColor()
childView = ChildView()
childView.backgroundColor = UIColor.redColor()
containerView.translatesAutoresizingMaskIntoConstraints = false
childView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(childView)
view.addSubview(containerView)
// Constraints
let containerWidthNumber: CGFloat = 200
let childWidthNumber: CGFloat = 100
let containerWidth = NSLayoutConstraint(item: containerView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: containerWidthNumber)
let containerHeight = NSLayoutConstraint(item: containerView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: containerWidthNumber)
containerView.addConstraints([
containerWidth,
containerHeight,
NSLayoutConstraint(item: childView, attribute: .CenterX, relatedBy: .Equal, toItem: containerView, attribute: .CenterX, multiplier: 1, constant: 0),
NSLayoutConstraint(item: childView, attribute: .Top, relatedBy: .Equal, toItem: containerView, attribute: .Top, multiplier: 1, constant: 0)
])
widthConstraint = NSLayoutConstraint(item: childView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: childWidthNumber)
let childHeight = NSLayoutConstraint(item: childView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: childWidthNumber)
childView.addConstraints([
widthConstraint,
childHeight,
])
view.addConstraints([
NSLayoutConstraint(item: containerView, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: 0),
NSLayoutConstraint(item: containerView, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: 0),
])
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
self.widthConstraint.constant = 50
UIView.animateWithDuration(10, animations: {
self.childView.layoutIfNeeded()
}, completion: { (success: Bool) in
if success {
print("good")
}
})
}
}

Resources