View's Frame get called twice and causes clipping bug - ios

I have a base UIView that has a visualEffectView as it's subview. I set the visualEffectView's view frame.height as 80. When presenting to ViewController, I want to reset its height to 120. Currently, when presented, the height is initially 120 but then quickly clips back down to 80. Shouldn't my frame in my ViewController override the frame set in the UIView? My frame in the ViewController gets called first but later it gets called from the UIView, causing the clipping bug.
class CardView: UIView {
let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
override init(frame: CGRect) {
super.init(frame: frame)
clipsToBounds = true
addSubview(visualEffectView)
}
override func layoutSubviews() {
super.layoutSubviews()
visualEffectView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 80)
}
}
class ViewController: ViewController {
let cardView: CardView = {
let cv = CardView()
cv.imageView.clipsToBounds = true
cv.layer.cornerRadius = 18
return cv
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let superviewFrame = view.frame.height
cardView.frame = CGRect(x: 10, y: superviewFrame / 8, width: view.frame.width - 20, height: superviewFrame - (superviewFrame / 5))
cardView.visualEffectView.anchor(top: cardView.topAnchor, left: cardView.leftAnchor, bottom: nil, right: cardView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 120)
}
}

try moving this code
visualEffectView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 80)
from override func layoutSubviews() { to override init(frame: CGRect) {
after super.init()

You may need a guard condition and if there is a constraint, just pass.
override func layoutSubviews() {
super.layoutSubviews()
guard visualEffectView.constraintsAffectingLayout(for: .vertical).count == 0 else {return}. // Here
visualEffectView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 80)
}

Related

UIScrollView inside of UIViewController not scrolling horizontally

I have created a UIScrollView inside of a UIViewController. I have added a UIView inside of my UIScrollView with one label inside of the UIView. The plan is to eventually add 3 very large UIButtons that is why I need a scroll view. I have not used storyboard and it is done all programmatically. I cannot get the UIScrollView to work.
class ChooseCategoryPostViewController: UIViewController, UIScrollViewDelegate {
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.backgroundColor = .green
return sv
}()
let myView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
let test: UILabel = {
let label = UILabel()
label.text = "test label"
label.font = UIFont.boldSystemFont(ofSize: 15)
label.textAlignment = .center
return label
}()
override func viewDidLoad() {
view.addSubview(scrollView)
scrollView.addSubview(myView)
scrollView.anchor(top: topTitle.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 200, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: view.frame.width, height: 200)
myView.anchor(top: scrollView.topAnchor, left: scrollView.leftAnchor, bottom: scrollView.bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 10, paddingBottom: 0, paddingRight: 0, width: 1700, height: 200)
myView.addSubview(test)
test.anchor(top: myView.topAnchor, left: nil, bottom: nil, right: myView.rightAnchor, paddingTop: 20, paddingLeft: 0, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
}
override func viewDidLayoutSubviews() {
scrollView.isScrollEnabled = true
// Do any additional setup after loading the view
scrollView.contentSize = CGSize(width: view.frame.width, height: 200)
}
You need to make sure that the width of the scroll view contentSize is greater than the width of the scroll view itself. Something as simple as the following should cause horizontal scrolling to happen:
override func viewDidLayoutSubviews() {
scrollView.isScrollEnabled = true
// Do any additional setup after loading the view
scrollView.contentSize = CGSize(width: view.frame.width * 2, height: 200)
}

Apple MapKit UI - TableView // No storyboard

Attempted to make a mock up of the apple maps UI but whenever I do I table view on top of my search input view the UI doesn't format correctly. when I comment the table view the UI works according and doesn't look a mess. I'm 100% sure its the table view but not sure why this would be the case. Does anyone have any apple maps mock ups code? - I also program without storyboards on UIKit
func configureViewComponents() {
backgroundColor = .white
addSubview(indicatorView)
indicatorView.anchor(top: topAnchor, left: nil, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 40, height: 8)
indicatorView.centerX(inView: self)
configureSearchBar()
configureTableView()
configureGestureRecognizers()
}
func configureSearchBar() {
searchBar = UISearchBar()
searchBar.placeholder = "Search for a place or address"
searchBar.delegate = self
searchBar.barStyle = .black
searchBar.setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
addSubview(searchBar)
searchBar.anchor(top: indicatorView.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 8, paddingBottom: 0, paddingRight: 8, width: 0, height: 50)
}
func configureTableView() {
tableView = UITableView()
tableView.rowHeight = 72
tableView.delegate = self
tableView.dataSource = self
tableView.register(SearchCell.self, forCellReuseIdentifier: reuseIdentifier)
addSubview(tableView)
tableView.anchor(top: searchBar.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 8, paddingLeft: 0, paddingBottom: 100, paddingRight: 0, width: 0, height: 0)
}
Tableview Code
UI
UI without the Table view code

Update UICollectionView header height based on headers UITextview content

I have a UICollectionViewController header cell that displays information for a business.
A business may or may not have a description.
In my header cell I set a business object, and then set all the text for the labels etc. In here I also try calculating the required text field height based off of this post
What I need is to actually set my header cells height dynamically in the UICollectionViewController where it is used.
BusinessDetailHeader class:
var maxTextViewHeight: CGFloat = 0
var business: Business? {
didSet {
guard let imageUrl = business?.logoUrl else {return}
let url = URL(string: imageUrl)
logoView.kf.setImage(with: url)
headerImageView.kf.setImage(with: url)
guard let businessName = business?.name else {return}
guard let address = business?.address else {return}
guard let hoursOfOperation = business?.hoursOfOperation else {return}
guard let phoneNumber = business?.phoneNumber else {return}
businessNameLabel.text = businessName
addressLabel.text = address
hoursofOperationLabel.text = hoursOfOperation
phoneNumberLabel.text = phoneNumber
if let description = business?.description {
detailsTextField.text = description
let amountOfLinesToBeShown:CGFloat = 6
if let height = detailsTextField.font?.lineHeight {
maxTextViewHeight = height * amountOfLinesToBeShown
}
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
layoutViews()
}
fileprivate func layoutViews() {
addSubview(headerImageView)
headerImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 130)
//ADJUST DETAIL HEIGHT HERE BASED ON CALCULATED HEIGHT FOR TEXTVIEW
addSubview(detailView)
detailView.anchor(top: headerImageView.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 200+maxTextViewHeight)
detailView.addSubview(businessNameLabel)
businessNameLabel.anchor(top: logoView.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
businessNameLabel.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
detailView.addSubview(detailsTextField)
detailsTextField.anchor(top: businessNameLabel.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: maxTextViewHeight)
detailView.addSubview(addressLabel)
addressLabel.anchor(top: detailsTextField.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
addressLabel.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
detailView.addSubview(hoursofOperationLabel)
hoursofOperationLabel.anchor(top: addressLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
hoursofOperationLabel.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
detailView.addSubview(phoneNumberLabel)
phoneNumberLabel.anchor(top: hoursofOperationLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
phoneNumberLabel.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
}
The height is updated properly with no issues but sometimes when the height is too big, the text and all the following elements overflow out of the header cell. I think this has to do with how I address the height of the cell in my UICollecitonViewController.
I set it's height like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: view.frame.width, height: 330)
}
The height of 330 here needs to be dynamic. Is this possible? Should I be setting the height property like this? I've seen posts like this one that try addressing this, but the solutions don't seem like they are the best.
Please check this link, it will be really helpful in how to make UIcollectionView header dynamic height?. Basically you need to set autolayout constraint according to your requirement; Need to call setNeedlayout() in referenceSizeForHeaderInSection (collectionViewFlowLayout delegate) method.

UIView wont appear

I have a function in my viewController that lays out my views using SnapKit. The funcion looks like this
#objc func setupView(){
//will add the scroll view to the subview
view.addSubview(scrollView)
scrollView.addSubview(imageContainer)
scrollView.addSubview(eventInfoVIew)
// scrollView.addSubview(currentEventImage)
scrollView.contentInsetAdjustmentBehavior = .never
scrollView.backgroundColor = .green
eventInfoVIew.backgroundColor = .red
imageContainer.backgroundColor = .blue
scrollView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
imageContainer.anchor(top: scrollView.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: view.frame.width, height: view.frame.height - 200)
eventInfoVIew.anchor(top: imageContainer.bottomAnchor, left: view.leftAnchor, bottom: scrollView.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.scrollIndicatorInsets = view.safeAreaInsets
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: view.safeAreaInsets.bottom, right: 0)
}
However when I run the app one of the views isn't there and it makes no sense because to the best of my knowledge I laid out the constraints properly. The eventInfoView is the one giving me problems.
Sorry forgot to add image of current screen
Have you try debugging with the view inspector? What happens with your views? A screenshot of the view hierarchy would help a lot.
Without this, my hunch is that you are not using enough constraints and therefore your layout is ambiguous. Don't forget that UIScrollView requires 6 constraints to layout properly instead of 4.
Edit: If you want to check if the scrollView is the issue, try adding this code inside
scrollView.snp.makeConstraints { (make) in
make.edges.equalTo(view)
make.width.height.equalTo(view)
}

Constraint Issue

So I have a cell that is setup to have a collectionView inside of it. The height for that cell is given in this function.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.section == 0 {
return CGSize(width: view.frame.width, height: 300)
}
return CGSize(width: view.frame.width, height: 280)
}
Inside the class for this cell I create a collectionView and again create a collection of cells inside of it.
#objc func setupViews(){
backgroundColor = .red
addSubview(categoryCollectionView)
addSubview(sectionNameLabel)
sectionNameLabel.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 14, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
categoryCollectionView.anchor(top: sectionNameLabel.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
categoryCollectionView.delegate = self
categoryCollectionView.dataSource = self
categoryCollectionView.showsHorizontalScrollIndicator = false
categoryCollectionView.register(CategoryEventCell.self, forCellWithReuseIdentifier: cellID)
}
Each cell has a background image and a title that is below the image. However the text is always somewhat cut off. My current constraints are here
#objc func setupViews(){
backgroundColor = .clear
setCellShadow()
addSubview(backgroundImageView)
addSubview(eventNameLabel)
backgroundImageView.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 9, paddingRight: 0, width: 0, height: 0)
eventNameLabel.anchor(top: backgroundImageView.bottomAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}
How would I change my constraints or text/element size to make sure everything fits?
My current view looks like so
You are pinning the image to the bottom of its cell, and the label has no say in how to fit in. Instead you should pin the image's to the label's top, and the label's bottom to the cell's bottom. Like this in the visual format language: [image]-[label]-|. You also need to edit the backgroundImageView's content compression resistance.
Here is the fixed version where the label causes the image's height to shrink upwards to fit in:
#objc func setupViews(){
backgroundColor = .clear
setCellShadow()
addSubview(backgroundImageView)
addSubview(eventNameLabel)
// ensure the image compresses, not the eventNameLabel, by lowering its priority
backgroundImageView.setContentCompressionResistancePriority(UILayoutPriority(600), for: .vertical)
backgroundImageView.anchor(top: topAnchor, left: leftAnchor, bottom: eventNameLabel.topAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
eventNameLabel.anchor(top: backgroundImageView.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}

Resources