parent UIView is not showing but the subviews are showing i nswift - swift

this is the code I wrote
let topViewa = UIView()
topViewa.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(topViewa)
topViewa.backgroundColor = .white
topViewa.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 300).isActive = true
topViewa.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
topViewa.frame.size = CGSize(width: screenWidth, height: 44)
let fw = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
fw.backgroundColor = .red
topViewa.addSubview(fw)
with screenWidth being the width of the screen.
and when I run this, this is what I get
Why am I not getting the parent UIView with white background?

Why am I not getting the parent UIView with white background?
Because the white view has no size.
The line
topViewa.frame.size = CGSize(width: screenWidth, height: 44)
...has no effect. You have elected to use constraints to position and size this view. Now you must fulfill that contract, giving it both position and size through constraints alone. You have not provided any height or width constraints (or alternatively, bottom or trailing constraints), so the view has zero size and you see nothing.
The red subview, meanwhile, remains visible, because the white superview's clipsToBounds is false. If it were true, you wouldn't see the red subview either.

Related

adjustsFontSizeToFitWidth not working with iOS 15 UIButton

I want to size the text on my button to automatically fit to the edges of the button.
This code was working pre-iOS15, now it doesn't.
testButton.titleLabel?.adjustsFontSizeToFitWidth = true
testButton.titleLabel?.minimumScaleFactor = 0.1
How can I automatically resize the text to fit the size of my button in iOS 15?
This is playground code for testing. It includes everything I've tried, but still isn't working.
import UIKit
import PlaygroundSupport
//building button
let testButton = UIButton(frame: CGRect(x: 100, y: 100, width: 300, height: 300))
testButton.setTitle("test", for: .normal)
//everything I've found in other answers that is supposed to work
var titleLabel = testButton.titleLabel!
titleLabel.font = UIFont.systemFont(ofSize: 300)
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.minimumScaleFactor = 0.1
titleLabel.numberOfLines = 1 //also tried 0 instead
titleLabel.lineBreakMode = .byClipping
//configuring button
testButton.configuration = UIButton.Configuration.filled()
//display the button
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
containerView.backgroundColor = .white
PlaygroundPage.current.liveView = containerView
containerView.addSubview(testButton)
Related questions I've already looked at and tried:
Best way to adjust font size with width and height of UILabel
Swift - Adjusting fontSize to fit the width of the layout (programmatically)
How to get .adjustsFontSizeToFitWidth to function properly
How to set font size to fill UILabel height?
Auto change the font size to fit the button in swift
UIButton auto-adjust Button font size Swift
I found out it could be caused by testButton.configuration = UIButton.Configuration.filled(), but I don't know why.

UIScrollView content inset defined by a height of a view outside of the subtree

Given a view hierarchy:
- View `container`
- UIScrollView `scrollView`
- UIView `content`
- UIView `footer`
I would like the UIScrollView.contentInset.bottom to be equal to footer.bounds.height.
Question: Can this be expressed using Auto Layout?
Now, there is a very evident brute-force approach that I am aware of and that works:
Observe changes to the bounds property of the footer
scrollView.contentInset.bottom = -footer.bounds.height once footer's parent has finished layoutSubviews().
Or alternatively I could have a constraint between content.bottom and scrollView.bottom (which, as, I'm sure, you are aware, indicates the bottom content inset for non-ambiguously size content) and have its constant altered each time the footer bounds change.
But the point is that all of those approaches are very on-the-nose, really makes me uncomfortable for the terrible code they produce so I am wondering:
Can this be expressed using Auto Layout?
I have attempted to do the following:
content.bottomAnchor.constraint(equalTo: footer.topAnchor)
Hoping that content.bottomAnchor would be treated as the bottom inset of the scroll view content, but nope - Auto Layout literally treats it as me constraining content's bottom to the footer's top.
OK - one approach...
As of iOS 11 (I'm assuming you don't need to target earlier than that), a subview of a UIScrollView can be constrained to the scroll view's Frame Layout Guide. This made it easy to add non-scrolling UI elements to the scroll view hierarchy.
Based on this hierarchy:
- view
- scrollView
- contentView
- element1
- element2
- element3
- UILayoutGuide
- footerView
What we'll do is:
add all the "scrollable" elements to the contentView
plus add a UILayoutGuide to the contentView which will serve as or "bottom" scrollable element
add the footerView to the scrollView last so it is at the top of the z-order
constrain the footerView to the scrollView's Frame Layout Guide so it stays put
constrain the heightAnchor of our UILayoutGuide equal to the heightAnchor of the footerView
Because a UILayoutGuide is a non-rendering view, it will not be visible but it will create the space from the bottom of our last viewable element to the bottom of the contentView -- and it will automatically change height if/when the footerView changes height.
Here's a complete example - scrollView / contentView / 3 imageViews / layout guide / translucent footerView:
class ExampleViewController: UIViewController {
let scrollView: UIScrollView = {
let v = UIScrollView()
v.backgroundColor = .lightGray
return v
}()
let contentView: UIView = {
let v = UIView()
v.backgroundColor = .cyan
return v
}()
let footerView: UILabel = {
let v = UILabel()
v.textAlignment = .center
v.textColor = .white
v.font = UIFont.systemFont(ofSize: 24.0, weight: .bold)
v.text = "Footer View"
v.backgroundColor = UIColor.black.withAlphaComponent(0.65)
return v
}()
var imgView1: UIImageView = {
let v = UIImageView()
v.backgroundColor = .red
v.image = UIImage(systemName: "1.circle")
v.tintColor = .white
return v
}()
var imgView2: UIImageView = {
let v = UIImageView()
v.backgroundColor = .green
v.image = UIImage(systemName: "2.circle")
v.tintColor = .white
return v
}()
var imgView3: UIImageView = {
let v = UIImageView()
v.backgroundColor = .blue
v.image = UIImage(systemName: "3.circle")
v.tintColor = .white
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// add 3 image views as the content we want to see
contentView.addSubview(imgView1)
contentView.addSubview(imgView2)
contentView.addSubview(imgView3)
// add contentView to srollView
scrollView.addSubview(contentView)
// add footer view to scrollView last so it's at the top of the z-order
scrollView.addSubview(footerView)
view.addSubview(scrollView)
[scrollView, contentView, footerView, imgView1, imgView2, imgView3].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
}
// "spacer" for bottom of scroll content
// we'll constrain it to the height of the footer view
let spacerGuide = UILayoutGuide()
contentView.addLayoutGuide(spacerGuide)
let g = view.safeAreaLayoutGuide
let svCLG = scrollView.contentLayoutGuide
let scFLG = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
// constrain scrollView view 40-pts on all 4 sides to view (safe-area)
scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0),
// contentView view 0-pts top / leading / trailing / bottom to scrollView contentLayoutGuide
contentView.topAnchor.constraint(equalTo: svCLG.topAnchor, constant: 0.0),
contentView.leadingAnchor.constraint(equalTo: svCLG.leadingAnchor, constant: 0.0),
contentView.trailingAnchor.constraint(equalTo: svCLG.trailingAnchor, constant: 0.0),
contentView.bottomAnchor.constraint(equalTo: svCLG.bottomAnchor, constant: 0.0),
// contentView width == scrollView frameLayoutGuide width
contentView.widthAnchor.constraint(equalTo: scFLG.widthAnchor, constant: 0.0),
// imgView1 to top of contentView
imgView1.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0.0),
// imgView1 width / height
imgView1.widthAnchor.constraint(equalToConstant: 240.0),
imgView1.heightAnchor.constraint(equalToConstant: 240.0),
// imgView1 centerX to contentView centerX
imgView1.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// imgView2 top to bottom of imgView1 + 20-pt spacing
imgView2.topAnchor.constraint(equalTo: imgView1.bottomAnchor, constant: 20.0),
// imgView2 width / height
imgView2.widthAnchor.constraint(equalToConstant: 200.0),
imgView2.heightAnchor.constraint(equalToConstant: 280.0),
// imgView2 centerX to contentView centerX
imgView2.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// imgView3 top to bottom of imgView2 + 20-pt spacing
imgView3.topAnchor.constraint(equalTo: imgView2.bottomAnchor, constant: 20.0),
// imgView3 width / height
imgView3.widthAnchor.constraint(equalToConstant: 280.0),
imgView3.heightAnchor.constraint(equalToConstant: 320.0),
// imgView3 centerX to contentView centerX
imgView3.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// spacerGuide top to bottom of actual content
// spacerGuide top to imgView3 bottom
spacerGuide.topAnchor.constraint(equalTo: imgView3.bottomAnchor, constant: 0.0),
// spacerGuide to leading / trailing / bottom of contentView
spacerGuide.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0.0),
spacerGuide.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0.0),
spacerGuide.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0.0),
// footerView to leading / trailing / bottom of scrollView frameLayoutGuide
// (constrained to frameLayoutGuide so it won't scroll)
footerView.leadingAnchor.constraint(equalTo: scFLG.leadingAnchor, constant: 0.0),
footerView.trailingAnchor.constraint(equalTo: scFLG.trailingAnchor, constant: 0.0),
footerView.bottomAnchor.constraint(equalTo: scFLG.bottomAnchor, constant: 0.0),
// footerView height == scrollView height with 0.25 multiplier
// (so it will change height when scrollView changes height, such as device rotation)
footerView.heightAnchor.constraint(equalTo: scFLG.heightAnchor, multiplier: 0.25),
// finally, spacerGuide height equal to footerView height
spacerGuide.heightAnchor.constraint(equalTo: footerView.heightAnchor),
])
}
}
Result:
Scrolled to the bottom:
and rotated (so we see the footerView height change) scrolled all the way to the bottom:
Edit
The answer to the specific question is: you can't.
A scroll view's contentInset is not an object to which you can add constraints... it's a Property of the scroll view. Much like you could not constrain a scroll view's .backgroundColor to an auto-layout constraint.
Landed here after looking for a solution, in my case the scroll view is actually a UICollectionView, so adding "helper" elements to the layout (as suggested by another answer) would have been more challenging (changing dataSource logic etc)
I ended up doing the following:
(This example assumes you have a bottomView attached to the bottom of the screen and you want your scrollView / collectionView to be inset based on it)
Set scrollView.contentInsetAdjustmentBehavior = .never
Then in your View Controller, do this:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.scrollView.contentInset = .init(top: self.view.safeAreaLayoutGuide.layoutFrame.minY,
left: 0,
bottom: bottomView.frame.height,
right: 0)
}
Note that if you also want to account for left and right safe area (e.g. landscape avoiding iPhone notch), you can do:
[...]
left: self.view.safeAreaLayoutGuide.layoutFrame.minX
[...]
right: self.view.frame.width - self.view.safeAreaLayoutGuide.layoutFrame.maxX

Swift 5 UIScrollView adding background image

I'm trying to add a background image that will scroll with my vertical scrollView. Before adding the background image, my scrollView only scroll vertically, but after setting the background image, the scrollView can now scroll horizontally and vertically as if the background image's width is greater than the scrollview's width
let scrollView: UIScrollView = {
let view = UIScrollView()
view.translatesAutoresizingMaskIntoConstraints = false
view.isDirectionalLockEnabled = true
view.isScrollEnabled = true
return view
}()
let backgroundIV: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "background"))
imageView.contentMode = UIView.ContentMode.scaleToFill
return imageView
}()
The function I use to setup my view
private func setupContent() {
scrollViewHeight = viewHeight * 2
[scrollView].forEach { view.addSubview($0) }
scrollView.anchor(top: view.topAnchor, leading: view.leadingAnchor, bottom: view.bottomAnchor, trailing: view.trailingAnchor)
scrollView.contentSize = CGSize(width: 0, height: scrollViewHeight)
[backgroundIV].forEach { scrollView.addSubview($0) }
backgroundIV.anchor(top: scrollView.topAnchor, leading: scrollView.leadingAnchor, bottom: scrollView.bottomAnchor, trailing: scrollView.trailingAnchor, size: .init(width: 0, height: scrollViewHeight))
}
So how should i set my background image so that my scrollview won't scroll horizontally??
You have to set the right content sizes for your scrollView or adjust the content.
Set your imageView width equal to your scrollView's width. It will avoid scrollView horizontal scroll.
Please find the below code for setting the widthAchor.
imageView.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
After applying Vikram Parimi's answer and some further digging, there were two factors that effect my scrollview to become horizontal
The characteristics of constraints between Scroll View and view tells us: the alignment and the size are not contradicted anymore, both of which must be defined to eliminate ambiguity. Which I found out from https://medium.com/#tingyishih/ios-scrollview-constraints-8d8140d329a0
The other factor is that the bug only exist in iphone 11 pro max simulator and not iphone 8 simulator, which can be solve by using safeAreaLayoutGuide
if #available(iOS 11.0, *) {
imageView.widthAnchor.constraintEqualToAnchor(scrollView.safeAreaLayoutGuide.widthAnchor).active = true
} else {
imageView.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
}

set background color of footerView UI TableView doesn't work

I use UIView() for remove separate line and it works but changing background color of footer view doesn't work. This is my code. Can somebody explain why? Thank you so much!
let footerView = UIView()
footerView.backgroundColor = UIColor(hexString: "#F7F9FC")
myTableView.tableFooterView = footerView
It is because the size of your footerView is zero.
let footerView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 40.0))
footerView.backgroundColor = UIColor(hexString: "#F7F9FC")
myTableView.tableFooterView = footerView
I think you must set a frame for footer view. If you just only create UIView then the default of Apple will create UIView with frame is zero. You need to set width, height, x, y for it to display in TableView
let frame = CGRect(x: 0, y: 0, width: yourWidthExpect, height: yourHeightExpect))
let footerView = UIView(frame: frame)
After that you can set background color and do anything.

Why doesn't Autolayout change a views frame

I created a view in a func of its superview
let black = NSView(frame: NSRect(x: 0, y: 0, width: 10, height: 10))
black.translatesAutoresizingMaskIntoConstraints = false
black.wantsLayer = true
black.layer?.backgroundColor = NSColor.black.cgColor
self.addSubview(black)
black.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -15).isActive = true
black.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 15).isActive = true
black.heightAnchor.constraint(equalToConstant: 115).isActive = true
black.widthAnchor.constraint(equalToConstant: 115).isActive = true
print ("origin (\(black.frame.origin.x),\(black.frame.origin.y)) size (\(black.frame.size.width),\(black.frame.size.height)) ")
The output is origin(0,0), size(10,10), these are the values the view was created with.
On screen the black view is positioned as expected origin:(15,15) size:(115,115).
Why is the frame not updated?
After setting the constraints you can update the frame of your view calling the same of layoutIfNeeded function for Cocoa:
black.layoutSubtreeIfNeeded()