So, I added some text (UITextView) to my stackView and centered to the top. I also added a UIImageView which would sit nicely under my UITextView. Well it doesn't. For some reason the image covers the text completely. If I delete the image the text comes back up nice on the top center. Played a lot with the stack distribution and alignment but no luck. Not sure what I'm missing :(. Any help is appreciated!
I'm adding both the UITextView and UIIMageView as arrangedSubview to the stack.
Here is my code:
//stack
let stack: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.spacing = 5
stack.distribution = .fillProportionally
stack.alignment = .fill
return stack
}()
//text
fileprivate let title: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
title.contentMode = .scaleAspectFit
title.layer.cornerRadius = 10
title.backgroundColor = .darkGray
title.font = UIFont(name: "Megrim-Regular", size: 17)
title.textColor = .white
title.textAlignment = .center
return title
}()
//image
let image: UIImageView = {
let image = UIImageView()
image.image = UIImage(named: "demoPic.jpg")
image.translatesAutoresizingMaskIntoConstraints = false
image.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
return image
}()
Hope this below may help,
I think your issue is relating to constraints applied to the stackview and the holder view. (See below)
Your UI Elements (TextView & Image) code seems to be fine (maybe the image will not be work with 50 width /50 height inside this particular stack view configuration. It will require a different approach IMO.
Nevertheless on my playground in order to see it, I just applied 2 constraints towards my container view in order to see your TextView well above your ImageView as you wanted.
Here is the playground I used to reproduce your issue, you can copy and paste it to see if it fits what you request.
import UIKit
import PlaygroundSupport
/// DEMO VIEW CLASS
final class DemoView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented")
}
}
// YOUR UI CODE
//stack
let stack: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.spacing = 5
stack.distribution = .fillProportionally
stack.alignment = .fill
return stack
}()
//text
fileprivate let title: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
title.contentMode = .scaleAspectFit
title.layer.cornerRadius = 10
title.backgroundColor = .darkGray
title.font = UIFont(name: "Megrim-Regular", size: 17)
title.text = "TextView"
title.textColor = .white
title.textAlignment = .center
return title
}()
//image
let image: UIImageView = {
let image = UIImageView()
image.backgroundColor = .red
image.translatesAutoresizingMaskIntoConstraints = false
image.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
return image
}()
// PLAYGROUND DEMO VIEW TO HOLD YOUR STACK VIEW
let demoView = DemoView(frame: CGRect(x: 0, y: 0, width: 350, height: 150))
stack.addArrangedSubview(title)
stack.addArrangedSubview(image)
demoView.addSubview(stack)
demoView.addConstraints(
NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[stackView]-0-|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0),
metrics: nil,
views: ["stackView": stack])
)
demoView.addConstraints(
NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[stackView]-0-|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0),
metrics: nil,
views: ["stackView": stack])
)
PlaygroundPage.current.liveView = demoView
Results: Your Text View is above the image center (ImageView just have a RED Background here).
Related
I am trying to create the view porgrammatically as show in image below. However the whole thing scatter once I embed the three horizontal stack view in the vertical stack view . I have set the spacing and alignment but for some reason it’s not showing the views properly when i run it in playgroung; the spacing gets messy. How do I resolve this issue ? Please ignore the pencil image. Its just there as a placeholder
import UIKit
import SnapKit
import Foundation
import PlaygroundSupport
extension UIStackView {
func removeAllSubviews() {
arrangedSubviews.forEach { $0.removeFromSuperview() }
}
convenience init(frame: CGRect = .zero,
alignment: UIStackView.Alignment = .fill,
axis: NSLayoutConstraint.Axis = .vertical,
distribution: UIStackView.Distribution = .equalSpacing,
spacing: CGFloat = 0.0) {
self.init(frame: frame)
self.alignment = alignment
self.axis = axis
self.distribution = distribution
self.spacing = spacing
}
}
class UserCell: UIView {
private lazy var reputationLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 15, weight: .bold)
label.text = "Reputation"
return label
}()
private lazy var increasePerformaceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 10, weight: .semibold)
label.text = "Increase Performance"
return label
}()
private lazy var increasePerformaceImage : UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = UIImage(systemName: "pencil.circle.fill")
return imageView
}()
private lazy var reviewsImage : UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = UIImage(systemName: "pencil.circle.fill")
return imageView
}()
private lazy var reviewsLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
label.text = "Reviews"
return label
}()
private lazy var dateLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
label.text = "All Time as of 2020-10-10"
return label
}()
private lazy var scoreLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 15, weight: .semibold)
label.textColor = .darkGray
label.text = "325"
return label
}()
private lazy var ratingsLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
label.textColor = .darkGray
label.text = "Ratings"
return label
}()
private lazy var ratingsNumberLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
label.textColor = .darkGray
label.text = "4.5"
return label
}()
private lazy var ratingStarImage : UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = UIImage(systemName: "pencil.circle.fill")
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
addComponents()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func addComponents() {
let reputationStackView = UIStackView(frame: .zero, alignment: .top, axis: .horizontal, distribution: .equalCentering, spacing:25)
let reviewStackView = UIStackView(frame: .zero, alignment: .top, axis: .horizontal, distribution: .equalCentering, spacing: 25)
let ratingStackView = UIStackView(frame: .zero, alignment: .top, axis: .horizontal, distribution: .equalSpacing, spacing: 20)
let dateStackView = UIStackView(frame: .zero, alignment: .top, axis: .horizontal, distribution: .equalSpacing, spacing: 20)
let verticalStackView = UIStackView(frame: .zero, alignment: .top, axis: .vertical, distribution: .fill, spacing: 9)
reputationStackView.addArrangedSubview(reputationLabel)
reputationStackView.addArrangedSubview(increasePerformaceLabel)
reputationStackView.addArrangedSubview(increasePerformaceImage)
reviewStackView.addArrangedSubview(reviewsImage)
reviewStackView.addArrangedSubview(reviewsLabel)
reviewStackView.addArrangedSubview(scoreLabel)
dateStackView.addArrangedSubview(dateLabel)
ratingStackView.addArrangedSubview(ratingsLabel)
ratingStackView.addArrangedSubview(ratingsNumberLabel)
ratingStackView.addArrangedSubview(ratingStarImage)
verticalStackView.addArrangedSubview(reputationStackView)
verticalStackView.addArrangedSubview(reviewStackView)
verticalStackView.addArrangedSubview(dateStackView)
verticalStackView.addArrangedSubview(ratingStackView)
addSubview(verticalStackView)
increasePerformaceImage.snp.makeConstraints { (make) in
make.width.height.equalTo(20)
}
reviewsImage.snp.makeConstraints { (make) in
make.width.height.equalTo(20)
}
ratingStarImage.snp.makeConstraints { (make) in
make.width.height.equalTo(20)
}
verticalStackView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
}
let userCell = UserCell(frame: CGRect(x: 0, y: 0, width: 400, height: 100))
PlaygroundPage.current.liveView = userCell
As I mentioned in my comment... during layout development it can be a great help to give UI elements contrasting background colors to make it easy to see their frames at run-time.
Also, it really, really, REALLY helps to add comments in your code, so you know what you expect to happen.
Take a look at this modification to your UserCell class:
class UserCell: UIView {
private lazy var reputationLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 15, weight: .bold)
label.text = "Reputation"
return label
}()
private lazy var increasePerformaceLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 10, weight: .semibold)
label.textColor = .gray
label.text = "Increased Performance"
return label
}()
private lazy var increasePerformaceImage : UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(systemName: "pencil.circle.fill")
return imageView
}()
private lazy var reviewsImage : UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(systemName: "pencil.circle.fill")
return imageView
}()
private lazy var reviewsLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
label.text = "Reviews"
return label
}()
private lazy var dateLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
label.textColor = .gray
label.text = "All Time as of 2020-10-10"
return label
}()
private lazy var scoreLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 28, weight: .semibold)
label.textColor = .darkGray
label.text = "325"
return label
}()
private lazy var ratingsLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
label.textColor = .darkGray
label.text = "Rating"
return label
}()
private lazy var ratingsNumberLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
label.textColor = .darkGray
label.text = "4.5"
return label
}()
private var colorBackgrounds: Bool = false
convenience init(debug: Bool) {
self.init(frame: .zero)
self.colorBackgrounds = debug
addComponents()
}
private func addComponents() {
// horizontal stack view for the "top row" - alignment is Vertical Centering
let repStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 8.0)
repStack.addArrangedSubview(reputationLabel)
repStack.addArrangedSubview(increasePerformaceLabel)
repStack.addArrangedSubview(increasePerformaceImage)
// increasePerformaceLabel needs right alignment
increasePerformaceLabel.textAlignment = .right
// increasePerformaceImage 20x20
increasePerformaceImage.snp.makeConstraints { (make) in
make.width.height.equalTo(20.0)
}
// horizontal stack view for the "middle row" - alignment is Vertical Centering
let revStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 8.0)
// we'll use a vertical "sub stack" for the left-side of this "row" - alignment is Leading
let revLeftStack = UIStackView(frame: .zero, alignment: .leading, axis: .vertical, distribution: .fill, spacing: 2.0)
// we'll use a horizontal "sub stack" for the "top line" of the "left side" - alignment is Vertical Centering
let revLeftTopStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 4.0)
revLeftTopStack.addArrangedSubview(reviewsImage)
revLeftTopStack.addArrangedSubview(reviewsLabel)
revLeftStack.addArrangedSubview(revLeftTopStack)
revLeftStack.addArrangedSubview(dateLabel)
revStack.addArrangedSubview(revLeftStack)
revStack.addArrangedSubview(scoreLabel)
// reviewsImage 20x20
reviewsImage.snp.makeConstraints { (make) in
make.width.height.equalTo(20.0)
}
// horizontal stack view for the "bottom row" - alignment is Vertical Centering
let ratStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 8.0)
// horizontal stack view for the stars - alignment is Vertical Centering
let starStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 2.0)
ratStack.addArrangedSubview(ratingsLabel)
ratStack.addArrangedSubview(ratingsNumberLabel)
ratStack.addArrangedSubview(starStack)
// just for example, 4 filled stars and 1 half-filled star
for _ in 1...4 {
if let img = UIImage(systemName: "star.fill") {
let v = UIImageView(image: img)
v.snp.makeConstraints { (make) in
make.height.equalTo(16.0)
make.width.equalTo(v.snp.height).multipliedBy(1.2)
}
starStack.addArrangedSubview(v)
}
}
for _ in 5...5 {
if let img = UIImage(systemName: "star.lefthalf.fill") {
let v = UIImageView(image: img)
v.snp.makeConstraints { (make) in
make.height.equalTo(16.0)
make.width.equalTo(v.snp.height).multipliedBy(1.2)
}
starStack.addArrangedSubview(v)
}
}
// we want two "separator" lines
let sepLineViewA = UIView()
sepLineViewA.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
let sepLineViewB = UIView()
sepLineViewB.backgroundColor = sepLineViewA.backgroundColor
// vertical stack view to hold the "rows" and sep lines
let vs = UIStackView(frame: .zero, alignment: .center, axis: .vertical, distribution: .fill, spacing: 8.0)
vs.addArrangedSubview(repStack)
vs.addArrangedSubview(sepLineViewA)
vs.addArrangedSubview(revStack)
vs.addArrangedSubview(sepLineViewB)
vs.addArrangedSubview(ratStack)
// add vertical stack view to self
addSubview(vs)
// let's add a little top and bottom "padding"
// and fill the width
vs.snp.makeConstraints { (make) in
make.top.bottom.equalToSuperview().inset(8.0)
make.leading.trailing.equalToSuperview()
}
// set widths for "row" stack views and sep line views
repStack.snp.makeConstraints { (make) in
make.width.equalToSuperview().inset(16.0)
}
// top separator line extends full width
sepLineViewA.snp.makeConstraints { (make) in
make.width.equalToSuperview()
}
revStack.snp.makeConstraints { (make) in
make.width.equalTo(repStack.snp.width)
}
sepLineViewB.snp.makeConstraints { (make) in
make.width.equalTo(repStack.snp.width)
}
ratStack.snp.makeConstraints { (make) in
make.width.equalTo(repStack.snp.width)
}
// "row" stack views and sep line views heights
// Note: we do NOT set a height for the Reputation stack view
// this will make all three stack views the same heights
revStack.snp.makeConstraints { (make) in
make.height.equalTo(repStack.snp.height)
}
ratStack.snp.makeConstraints { (make) in
make.height.equalTo(repStack.snp.height)
}
// sep line views heights
sepLineViewA.snp.makeConstraints { (make) in
make.height.equalTo(1.0)
}
sepLineViewB.snp.makeConstraints { (make) in
make.height.equalTo(1.0)
}
if colorBackgrounds {
repStack.backgroundColor = UIColor(red: 1.0, green: 0.9, blue: 0.9, alpha: 1.0)
revStack.backgroundColor = UIColor(red: 0.9, green: 1.0, blue: 0.9, alpha: 1.0)
ratStack.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 1.0, alpha: 1.0)
reputationLabel.backgroundColor = .yellow
increasePerformaceLabel.backgroundColor = .cyan
reviewsLabel.backgroundColor = .green
dateLabel.backgroundColor = .yellow
scoreLabel.backgroundColor = .cyan
ratingsLabel.backgroundColor = .green
ratingsNumberLabel.backgroundColor = .yellow
}
}
}
extension UIStackView {
func removeAllSubviews() {
arrangedSubviews.forEach { $0.removeFromSuperview() }
}
convenience init(frame: CGRect = .zero,
alignment: UIStackView.Alignment = .fill,
axis: NSLayoutConstraint.Axis = .vertical,
distribution: UIStackView.Distribution = .equalSpacing,
spacing: CGFloat = 0.0) {
self.init(frame: frame)
self.alignment = alignment
self.axis = axis
self.distribution = distribution
self.spacing = spacing
}
}
and an example controller - we'll create Two instances, showing the "debug" colors in the second one:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
let testView = UserCell(debug: false)
testView.backgroundColor = .white
view.addSubview(testView)
let testViewD = UserCell(debug: true)
testViewD.backgroundColor = .white
view.addSubview(testViewD)
let g = view.safeAreaLayoutGuide
testView.snp.makeConstraints { (make) in
make.top.leading.trailing.equalTo(g).inset(16.0)
}
testViewD.snp.makeConstraints { (make) in
make.top.equalTo(testView.snp.bottom).offset(20.0)
make.leading.trailing.equalTo(testView)
}
[testView, testViewD].forEach { v in
v.layer.cornerRadius = 8.0
v.layer.shadowColor = UIColor.black.cgColor
v.layer.shadowRadius = 3.0
v.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
v.layer.shadowOpacity = 0.2
}
}
}
and here's the output:
Edit - in response to comment...
To learn about stack views...
Tip #1:
start with Apple's docs
head over to google and search for UIStackView tutorials
read through the many, many posts here on SO tagged UIStackView
Then experiment -- a LOT.
Tip #2:
I would also recommend not using SnapKit while learning about UI design and auto-layout. I know many people find it more convenient, but I've seen many beginners using it who don't really understand what it's doing -- and then have a lot of trouble getting the layout to look the way they want.
Tip #3:
As I mentioned previously -- comment your code with what you are expecting to happen. If it doesn't work, it's much easier to figure out why.
Tip #4:
Plan your layout, and then write your code logically to fit that plan. Not to claim that I write the best code in the world, but if you look at how I restructured your UserCell class you'll see that I set up the UI "sections" in order, and then put all the pieces together. That allows you to, for example, code the first section (in this case, the "top row"), run the app, and confirm it's laid out correctly. Then move on to the next section. If you try to code the entire layout all at once, it can be very difficult to realize that a piece of code 50 lines in is conflicting with an earlier line.
I want to put a Stackview of 5 images inside a UIView. Basically what I want is to make a rounded button with a shadow and inside the button 5 different small images horizontally.
What I already have is a viewcontroller with each a declaration and setup function. I am able to make the UIView.
Here is an image of what I am trying to achieve:
How do I solve this?
Some code I already have:
private let btnUIView: UIView = {
let btnUIView = UIView(frame: CGRect(x: 50, y: 50, width: 343, height: 77))
btnUIView.layer.shadowColor = UIColor.black.cgColor
btnUIView.layer.shadowOpacity = 0.5
btnUIView.layer.shadowOffset = .zero
btnUIView.layer.shadowRadius = 3
btnUIView.backgroundColor = .white
btnUIView.translatesAutoresizingMaskIntoConstraints = false
btnUIView.layer.borderWidth = 0
btnUIView.layer.borderColor = UIColor.gray.cgColor
return btnUIView
}()
private let btnStackView: UIStackView = {
let image = UIStackView()
image.translatesAutoresizingMaskIntoConstraints = false
image.distribution = .fillEqually
// image.spacing = 60
return image
}()
func setupBtnView(){
view.addSubview(btnUIView)
btnUIView.addSubview(btnStackView)
NSLayoutConstraint.activate([
btnUIView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
btnUIView.topAnchor.constraint(equalTo: btnText.bottomAnchor, constant: 15),
btnUIView.heightAnchor.constraint(equalToConstant: btnUIView.frame.height),
btnUIView.widthAnchor.constraint(equalToConstant: btnUIView.frame.width),
])
let imgone = UIImageView(image: #imageLiteral(resourceName: "imgtest1"))
let imgtwo = UIImageView(image: #imageLiteral(resourceName: "imgtest1"))
let imgthree = UIImageView(image: #imageLiteral(resourceName: "imgtest1"))
let imgfour = UIImageView(image: #imageLiteral(resourceName: "imgtst1"))
let imgfive = UIImageView(image: #imageLiteral(resourceName: "imgtest1"))
btnStackView.addArrangedSubview(imgone)
btnStackView.addArrangedSubview(imgtwo)
btnStackView.addArrangedSubview(imgthree)
btnStackView.addArrangedSubview(imgfour)
btnStackView.addArrangedSubview(imgfive)
imgone.contentMode = .scaleAspectFill
imgtwo.contentMode = .scaleAspectFill
imgthree.contentMode = .scaleAspectFill
imgfour.contentMode = .scaleAspectFill
imgfive.contentMode = .scaleAspectFill
imgone.clipsToBounds = true
imgtwo.clipsToBounds = true
imgthree.clipsToBounds = true
imgfour.clipsToBounds = true
imgfive.clipsToBounds = true
}
func setupImages(){
view.addSubview(btnStackView)
NSLayoutConstraint.activate([
btnStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
btnStackView.topAnchor.constraint(equalTo: btnUIView.bottomAnchor, constant: 20),
btnStackView.widthAnchor.constraint(equalToConstant: btnStackView.frame.width)
])
What I see in the app, an empty UIView:
This seems pretty straightforward. It took me about two minutes to write the code that gets us this:
That's just a sketchy rendering of what you're after, but it shows that the basic idea is simple enough. All I did was:
Create the outer view, configure it with a rounded border, and add it as a subview.
Create the stack view, configure it with equal spacing etc., and add it as a subview to the outer view.
Create five image views with images (just circles here) and add them as arranged subviews to the stack view.
Literally just 11 lines of code inside my viewDidLoad.
As for your code (which you have now shown), the chief problem is likely that the constraints make no sense. Here's my correction of your code (substituting my own circle image, for test purposes); this is the entire code of my test app view controller:
private let btnUIView: UIView = {
let btnUIView = UIView()
btnUIView.layer.shadowColor = UIColor.black.cgColor
btnUIView.layer.shadowOpacity = 0.5
btnUIView.layer.shadowOffset = .zero
btnUIView.layer.shadowRadius = 3
btnUIView.backgroundColor = .white
btnUIView.translatesAutoresizingMaskIntoConstraints = false
btnUIView.layer.borderWidth = 0
btnUIView.layer.borderColor = UIColor.gray.cgColor
return btnUIView
}()
private let btnStackView: UIStackView = {
let image = UIStackView()
image.translatesAutoresizingMaskIntoConstraints = false
image.distribution = .fillEqually
image.alignment = .center
return image
}()
override func viewDidLoad() {
super.viewDidLoad()
let r = UIGraphicsImageRenderer(size: CGSize(width: 30, height: 30))
let im = r.image { _ in UIBezierPath.init(ovalIn: CGRect(x: 1, y: 1, width: 28, height: 28)).stroke() }
let imgone = UIImageView(image: im)
let imgtwo = UIImageView(image: im)
let imgthree = UIImageView(image: im)
let imgfour = UIImageView(image: im)
let imgfive = UIImageView(image: im)
btnStackView.addArrangedSubview(imgone)
btnStackView.addArrangedSubview(imgtwo)
btnStackView.addArrangedSubview(imgthree)
btnStackView.addArrangedSubview(imgfour)
btnStackView.addArrangedSubview(imgfive)
setupBtnView()
}
func setupBtnView(){
view.addSubview(btnUIView)
btnUIView.addSubview(btnStackView)
NSLayoutConstraint.activate([
btnUIView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
btnUIView.topAnchor.constraint(equalTo: view.topAnchor, constant: 40),
btnUIView.heightAnchor.constraint(equalToConstant: 100),
btnUIView.widthAnchor.constraint(equalToConstant: 300),
])
NSLayoutConstraint.activate([
btnStackView.leadingAnchor.constraint(equalTo: btnUIView.leadingAnchor),
btnStackView.trailingAnchor.constraint(equalTo: btnUIView.trailingAnchor),
btnStackView.topAnchor.constraint(equalTo: btnUIView.topAnchor),
btnStackView.bottomAnchor.constraint(equalTo: btnUIView.bottomAnchor),
])
}
Result:
In my textfield there is a right view and datepicker added. When I select the date, text is overlapping with the right view.
I am using a Designable class for adding right view. How can I fix overlapping issue
Here is the code for setting up rightview
rightViewMode = UITextField.ViewMode.always
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 17, height: 17))
imageView.contentMode = .scaleAspectFit
imageView.image = rightImage
imageView.tintColor = color
// Added containerView for repositioning image
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 20))
self.addSubview(containerView)
containerView.addSubview(imageView)
rightView = containerView
Without seeing all your code it's kind of hard to tell what is going on here. Do you use storyboards and constraints? Or do you hardcode all the frames of your views?
What I would do here, is using a UIStackView and constraints, because it helps getting rid of all the hardcoded positioning values, and it gives you much more flexibility for laying out your UI.
let textField = UITextField()
textField.placeholder = "16 December 2018"
let imageView = UIImageView()
imageView.backgroundColor = .systemBlue
let stackView = UIStackView(arrangedSubviews: [textField, imageView])
stackView.axis = .horizontal
stackView.distribution = .fill
stackView.alignment = .center
stackView.spacing = 10
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 17).isActive = true
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor).isActive = true
As you can see I set the stack view's distribution property to .fill here, so because the width of your image is constrained to 17, your text field width will adjust to fill the width of the stack view. You may want to adjust this property, and the spacing property, depending on what kind of behaviour you're looking for.
I have solved my problem with this peace of code:
Added Padding For Textfields
let padding = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
I have a UIStackView which needs a background color in one of the stacks, so I placed a UIView inside, but the UIView is never displayed. The UIView currently has one subview, a UILabel, but should eventually have another UIStackView instead. If I display the UILabel as a direct child of the UIStackView, rather than the UIView, then the UILabel displays properly. So, what constraints are missing or wrong on my UIView?
let stack = UIStackView()
stack.axis = .Vertical
stack.alignment = .Leading
stack.distribution = .EqualCentering
stack.spacing = 0
let width = UIScreen.mainScreen().bounds.size.width
let frame = CGRect(x: 0, y: 0, width: width, height: 50)
let viewHolder = UIView(frame: frame)
viewHolder.backgroundColor = blueColor
//Needs a blue background
let name = UILabel()
name.text = nameText
name.textColor = yellowColor
name.backgroundColor = blueColor
name.font = UIFont.boldSystemFontOfSize(32.0)
let address = UILabel()
address.text = addressText
let dateTime = UILabel()
dateTime.text = calString
viewHolder.addSubview(name)
stack.addArrangedSubview(viewHolder)
stack.addArrangedSubview(address)
stack.addArrangedSubview(dateTime)
self.stackView.addArrangedSubview(stack)
And when run it produces this:
If I remove the UIView from the equation, ie:
let stack = UIStackView()
stack.axis = .Vertical
stack.alignment = .Leading
stack.distribution = .EqualCentering
stack.spacing = 0
let width = UIScreen.mainScreen().bounds.size.width
let frame = CGRect(x: 0, y: 0, width: width, height: 50)
let viewHolder = UIView(frame: frame)
viewHolder.backgroundColor = blueColor
//Needs a blue background
let name = UILabel()
name.text = nameText
name.textColor = yellowColor
name.backgroundColor = blueColor
name.font = UIFont.boldSystemFontOfSize(32.0)
let address = UILabel()
address.text = addressText
let dateTime = UILabel()
dateTime.text = calString
//viewHolder.addSubview(name)
stack.addArrangedSubview(name) //Add the label directly to the UIStackView
stack.addArrangedSubview(address)
stack.addArrangedSubview(dateTime)
self.stackView.addArrangedSubview(stack)
I get:
I just need the blue part to stretch across the screen
So, answer is, don't use UIStackView. Use the built in UITableView for this kind of layout.
navigationItem.hidesBackButton = true
navigationItem.leftBarButtonItem = nil
head = UIView()
head.frame = CGRectMake(0, 0, 200, 44)
head.frame.origin.x = CGFloat(0)
navigationItem.titleView = head
I attempt to align the titleView to the left, but it still remains in the middle.
Try this:
let title = UILabel()
title.text = "TITLE"
let spacer = UIView()
let constraint = spacer.widthAnchor.constraint(greaterThanOrEqualToConstant: CGFloat.greatestFiniteMagnitude)
constraint.isActive = true
constraint.priority = .defaultLow
let stack = UIStackView(arrangedSubviews: [title, spacer])
stack.axis = .horizontal
navigationItem.titleView = stack
The idea is that main view (in this case title) will take all the space it needs and spacer view will take all the free space left.
I figured it out.
I just need to set my custom UIView as the leftBarButtonItem.
I had a similar requirement of adding the Title along with the subtitle to the left of the navbar. I couldn't achieve it with a TitleView since it cannot be aligned left.
So I took #TIMEZ and #Wain's answers, along with responses from the thread here and added a complete answer, in case it helps anyone :
let titleLabel = UILabel()
titleLabel.text = "Pillars"
titleLabel.textAlignment = .center
titleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.headline)
let subtitleLabel = UILabel()
subtitleLabel.text = "How did you do today?"
subtitleLabel.textAlignment = .center
subtitleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.subheadline)
let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
stackView.distribution = .equalSpacing
stackView.alignment = .leading
stackView.axis = .vertical
let customTitles = UIBarButtonItem.init(customView: stackView)
self.navigationItem.leftBarButtonItems = [customTitles]
You can't align a title view to the left. You can create a title view and add a subview positioned to its left. If you're looking to display in place of the back button then you should be using a bar button item instead of title view.
I don't think Apple wants you to do that. Navigation bars have a pretty specific purpose that often involves having something else in the top left corner like a Back button. You might be better off making a custom UIView or UIToolbar that looks like the navigation bar.
If it's a custom UIView, override intrinsicContentSize and return
CGSize(width: .greatestFiniteMagnitude, height: UIView.noIntrinsicMetric)
This will stretch the view to the entire width between left and right bar button items.
You can constraint the titleView to the navigationBars leftAnchor.
private func setupNavigationBarTitleView() {
let titleView = YourCustomTitleView()
navigationBarTitleView = titleView
navigationBarTitleView?.translatesAutoresizingMaskIntoConstraints = false
navigationItem.titleView = navigationBarTitleView
if let navigationBar = navigationController?.navigationBar {
NSLayoutConstraint.activate([
titleView.leadingAnchor.constraint(equalTo: navigationBar.leadingAnchor, constant: 16),
titleView.heightAnchor.constraint(equalToConstant: 36)
])
}
}
The simplest solution is to add low priority constraint for title width.
let titleLabel = UILabel()
titleLabel.textAlignment = .left
...
let c = titleLabe.widthAnchor.constraint(equalToConstant: 10000)
c.priority = .required - 1
c.isActive = true
navigationItem.titleView = titleLabel
Hey guys after trying most of the solutions above. I found that most of em still did not meet my prod requirements. Here is a solution I came up with after trying out different solutions.
func setLeftAlignTitleView(font: UIFont, text: String, textColor: UIColor) {
guard let navFrame = navigationController?.navigationBar.frame else{
return
}
let parentView = UIView(frame: CGRect(x: 0, y: 0, width: navFrame.width*3, height: navFrame.height))
self.navigationItem.titleView = parentView
let label = UILabel(frame: .init(x: parentView.frame.minX, y: parentView.frame.minY, width: parentView.frame.width, height: parentView.frame.height))
label.backgroundColor = .clear
label.numberOfLines = 2
label.font = font
label.textAlignment = .left
label.textColor = textColor
label.text = text
parentView.addSubview(label)
}
let navLabel = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.width - 32, height: view.frame.height))
navLabel.text = "Hi, \(CurrentUser.firstName)"
navLabel.textColor = UIColor.white
navLabel.font = UIFont.systemFont(ofSize: 20)
navigationItem.titleView = navLabel