UIStackView not changing the width
UIStackView not changing the width
I have a UIStackView
with UIImageViews
that might be from 1 to 5. Each UIImageView
has an image coming from an array (from previously downloaded and cached images) and I'd like to keep the UIImageView
s stay in a perfect circle. I change the width
constant of the UIStackView
along with the spacing between the images in a way to overlap if there are more than 3 images.
UIStackView
UIImageViews
UIImageView
UIImageView
width
UIStackView
I had writen this code in a previous project and it works perfectly fine, but for some reason, when I call the changeNavStackWidth
function to change the width
of the UIStackView
, the width is not being updated and I have no clue why.
changeNavStackWidth
width
UIStackView
var userImages = [String]()
var navStackView : UIStackView =
let stack = UIStackView()
stack.axis = .horizontal
stack.alignment = .fill
stack.distribution = .fillEqually
stack.translatesAutoresizingMaskIntoConstraints = false
return stack
()
override func viewDidLoad()
super.viewDidLoad()
setupNavStack()
navBarStackTapGesture()
func setupNavStack()
guard let navController = navigationController else return
navController.navigationBar.addSubview(navStackView)
// x, y, w, h
navStackView.widthAnchor.constraint(equalToConstant: 95).isActive = true
navStackView.centerYAnchor.constraint(equalTo: navController.navigationBar.centerYAnchor).isActive = true
navStackView.heightAnchor.constraint(equalToConstant: 35).isActive = true
navStackView.centerXAnchor.constraint(equalTo: navController.navigationBar.centerXAnchor).isActive = true
func setNavBarImages()
for image in userImages
let imageView = UIImageView()
// imageView.image = UIImage(named: image)
let photoURL = URL(string: image)
imageView.sd_setImage(with: photoURL)
imageView.layer.borderColor = UIColor.white.cgColor
imageView.layer.borderWidth = 1
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
navStackView.addArrangedSubview(imageView)
navStackView.layoutIfNeeded()
switch userImages.count
case 0:
print("0 images")
case 1:
changeNavStackWidth(constant: 35, spacing: 0)
//changeNavStackWidth(constant: 60, spacing: 0)
case 2:
changeNavStackWidth(constant: 80, spacing: 10)
case 3:
changeNavStackWidth(constant: 95, spacing: -5)
case 4:
changeNavStackWidth(constant: 110, spacing: -10)
case 5:
changeNavStackWidth(constant: 95, spacing: -20)
case 6...1000:
// changeNavStackWidth(constant: 95, spacing: -20)
default:
print("default")
navigationItem.titleView = navStackView
navStackView.layoutIfNeeded()
func changeNavStackWidth(constant: CGFloat, spacing: CGFloat)
navStackView.constraints.forEach constraint in
if constraint.firstAttribute == .width
constraint.constant = constant
print("constan is:", constant) // not being printed
navStackView.spacing = spacing
override func viewDidLayoutSubviews()
super.viewDidLayoutSubviews()
navStackView.subviews.forEach $0.layer.cornerRadius = $0.frame.height / 2
@SahilManchanda in the declaration of the
navStackView
(I've added it in the code to make it clear)– Bold Lion
Sep 14 '18 at 18:37
navStackView
Can you check if your changeNavStackWidth function is getting called
– Sahil Manchanda
Sep 14 '18 at 18:43
@SahilManchanda I did, I've put a breakpoint on it and it's called, but for some reason it doesn't iterate through the constraints and I don't know why. When I step into it, it goes into the declaration of
navStackView
then goes back in the function straight to navStackView.spacing = spacing
and doesn't loop through the constraints. This is why that print
statement is never executed.– Bold Lion
Sep 14 '18 at 18:46
navStackView
navStackView.spacing = spacing
print
if possible can you put minimum working code in the question so that i can paste and run.. it will help in debug
– Sahil Manchanda
Sep 14 '18 at 18:52
3 Answers
3
based on our discussion in chat. Here is what you want:
A) if there is only one Image
B) if there are two Images:
C) if there are more than 2:
Code:
import UIKit
class TestStack: UIViewController
var userImages = [#imageLiteral(resourceName: "images-1"),#imageLiteral(resourceName: "images-2"),#imageLiteral(resourceName: "images-4"),#imageLiteral(resourceName: "images-5")]
let imagesHolder: UIView =
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
()
override func viewDidLoad()
super.viewDidLoad()
userImages.removeLast(2)
guard let bar = navigationController?.navigationBar else return
bar.addSubview(imagesHolder)
let size = 25
for i in 0..<userImages.count
var x = i * size
x = userImages.count > 2 ? x/2 : x
x = (i == 1 && userImages.count == 2) ? x + 5 : x
let imageView = UIImageView(frame: CGRect(x: x, y: 0, width: size, height: size))
imageView.image = userImages[i]
imageView.clipsToBounds = true
imageView.layer.cornerRadius = CGFloat(size / 2)
imagesHolder.addSubview(imageView)
let width = CGFloat((userImages.count * size)/2)
NSLayoutConstraint.activate([
imagesHolder.centerYAnchor.constraint(equalTo: bar.centerYAnchor),
imagesHolder.centerXAnchor.constraint(equalTo: bar.centerXAnchor),
imagesHolder.widthAnchor.constraint(equalToConstant: width),
imagesHolder.heightAnchor.constraint(equalToConstant: CGFloat(size))
])
Excellent! Thank you again!
– Bold Lion
Sep 14 '18 at 20:23
Instead of adjusting the StackView's width to adjust to the images:
top
bottom
center horizontally
aspect ratio
1:1
The constraints on the UIImageView in relation to the UIView will keep the images separated out equally, and the aspect ratio constraint on itself will keep the UIImage as a perfect circle.
What about the
UIView
constraints? Should they be top
, bottom
, leading
& trailing
to the UIStackView
? (PS: keep in mind, this UIStackView
is placed in the middle of the UINavigationBar
)– Bold Lion
Sep 14 '18 at 19:56
UIView
top
bottom
leading
trailing
UIStackView
UIStackView
UINavigationBar
Just
top
and bottom
constraints to the UIStackView. Leading/Trailing constraints and widths of views are based on your settings of the StackView (width, spacing, etc.)– BlondeSwan
Sep 15 '18 at 1:11
top
bottom
Changing your imageView's content mode to .aspectFit
will also do the trick without having to mess with your stackView's width.
.aspectFit
Change this imageView.contentMode = .scaleAspectFill
to this imageView.contentMode = .scaleAspectFit
imageView.contentMode = .scaleAspectFill
imageView.contentMode = .scaleAspectFit
AspectFill
stretches the image to fit the view, and doesn't maintain the scale ratio of the imageAspectFit
stretches the image until the horizontal bound OR vertical bound hits the view, while maintaining the scale ratio of the image.
AspectFill
AspectFit
see here
The problem with this is that I cannot set the image to be a circle. Since the
UIImageView
is set dynamically, I have to create a new one for each image, but while doing so, the frame is still not set, so I cant get the frame.height / 2
. I went through that issue today. This is why I overrride viewDidLayoutSubviews()
, iterate through the subviews of the navStackView
and then set it.– Bold Lion
Sep 14 '18 at 19:17
UIImageView
frame.height / 2
viewDidLayoutSubviews()
navStackView
Okay, I was thinking your actual image was a circle. In that case, check out the other answer I posted. You won't need to mess with the width of the StackView if you implement it that way.
– BlondeSwan
Sep 14 '18 at 19:27
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
where did you set navStackView.translatesAutoresizingMaskIntoConstraints = false
– Sahil Manchanda
Sep 14 '18 at 18:35