I have an HStack of content like title, description, etc. Then I have some rectangles I want to add to the UIStackView programatically. But they do not appear.
In Playground in Swift 4
It works when appended to the overall view:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
self.view = view
// Add rects
let rectFrame: CGRect = CGRect(x:0, y:0, width:100, height:16)
let stepRect = UIView(frame: rectFrame)
stepRect.backgroundColor = UIColor.darkGray
let rectFrame2: CGRect = CGRect(x:50, y:16, width:100, height:16)
let stepRect2 = UIView(frame: rectFrame2)
stepRect2.backgroundColor = UIColor.red
self.view.addSubview(stepRect)
self.view.addSubview(stepRect2)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
This returns a white screen with two rectangles, one gray and one red, stacked on top of each other with some offset.
But is not working when appended to a UIStackView:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
self.view = view
// Make vstack
let vStack = UIStackView()
vStack.axis = .vertical
vStack.frame = CGRect(x: 0, y: 0, width: 300, height: 20)
// Add rects
let rectFrame: CGRect = CGRect(x:0, y:0, width:100, height:16)
let stepRect = UIView(frame: rectFrame)
stepRect.backgroundColor = UIColor.darkGray
let rectFrame2: CGRect = CGRect(x:50, y:16, width:100, height:16)
let stepRect2 = UIView(frame: rectFrame2)
stepRect2.backgroundColor = UIColor.red
vStack.addArrangedSubview(stepRect)
vStack.addArrangedSubview(stepRect2)
view.addSubview(vStack)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
This returns a white screen.
Yes, you can “add a UIView to a UIStackView.
But the whole point of a stack view is to let the placement of these “arranged” subviews. One supplies a distribution of the subviews (defaults to .fill) and an alignment of the subviews (again, defaults to .fill).
But you have specified ambiguous frames for the stack view and its subviews (which don’t happen to have any intrinsic size). Also, the notion of different x coordinates in a vertical stack view doesn't really make sense, either, as the alignment dictates the placement. If you run this in an actual app and then use the “view debugger”, it will report this ambiguity:
Unfortunately, playgrounds don't supply this sorts of diagnostic.
So, if using stack views, you either supply views with intrinsic sizes and let the stack view dictate the size and placement, or define supply the stack view subviews with size constraints and then ask it to manage the spacing between them (by using distribution of .equalSpacing or .equalCentering).
And if you really want to set the x and y values for your views manually, then it doesn't make sense to use a stack view at all.
A few examples: Perhaps you want define the two subviews to have the same size as each other, spaced 10 pt apart, for a total height of 50 pt:
let stackView = UIStackView(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.spacing = 10
Or perhaps you want your two subviews of specific size spaced within this 50 pt tall stack view:
let stackView = UIStackView(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
stackView.axis = .vertical
stackView.distribution = .equalSpacing
stackView.alignment = .center
let grayView = UIView()
grayView.translatesAutoresizingMaskIntoConstraints = false
grayView.backgroundColor = .darkGray
let redView = UIView()
redView.translatesAutoresizingMaskIntoConstraints = false
redView.backgroundColor = .red
stackView.addArrangedSubview(grayView)
stackView.addArrangedSubview(redView)
view.addSubview(stackView)
NSLayoutConstraint.activate([
redView.widthAnchor.constraint(equalToConstant: 100),
redView.heightAnchor.constraint(equalToConstant: 16),
grayView.widthAnchor.constraint(equalToConstant: 100),
grayView.heightAnchor.constraint(equalToConstant: 16)
])
There are lots of other examples we could give you, but, in short, it simply doesn’t make sense to set frames of stack view and arranged subviews (esp with different x values) and hoping that the stack view will be able to reconcile this solely with with .fill distribution.
From Apple's UIStackView documentation:
For all distributions except the UIStackView.Distribution.fillEqually
distribution, the stack view uses each arranged view’s
intrinsicContentSize property when calculating its size along the
stack’s axis. UIStackView.Distribution.fillEqually resizes all the
arranged views so they are the same size, filling the stack view along
its axis. If possible, the stack view stretches all the arranged views
to match the view with the longest intrinsic size along the stack’s
axis.
Your UIViews have no content and so when the stack views lay them out it will not see any intrinsicContentSize it needs to preserve. To see a different behavior that shows how a stack view handles its arranged subviews, try putting non-empty text views or image views in the stack. You can also try setting
vStack.distribution = .fillEqually
If you want to be able to have a generic UIView hold the size you set, one way is to create your own minimal subclass of UIView and override its intrinsicContentSize property, returning the view's self.bounds.size
This is the code I'm using to set an image in the title of the nativation bar,
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
imageView.contentMode = .scaleAspectFit
let image = UIImage(named: "fullLogo")
imageView.image = image
self.navigationItem.titleView = imageView;
The issue is that image appears a little too big. How do I resize the image in code? I tried many combinations of the width and height but it doesn't seem to work.
According to documentation:
Custom title views are centered on the navigation bar and may be resized to fit.
That's what happening with your image view. The solution can be to add imageView as subview to UIView and assign UIView to titleView property.
The easier solution may be to set imageView.contentMode to .center but in this case you need to be sure that your fullLogo image is actually 100x30, if it is bigger than titleView frame calculated by iOS your image will overlay navigation bar and view controller's view.
And you can init your UIImageView or container UIView with frame: .zero because it's frame will be recalculated in runtime.
Set
imageView.clipsToBounds = true
For resizing the image this SO Post would help. Hope this helps.
Happy Coding.
I required to set time where 88:88 is there within an image.
Any suggestions are welcome.
You need to display the image (without the text) inside an UIImageView, and add an UILabel as subview.
let image = UIImageView()
image.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
let label = UILabel()
label.text = "88:88"
image.addSubview(label)
label.frame = image.frame
label.textAlignment = .center
Note: Setting the label with the same frame as the image view and the text alignment to center, ensure the label is centered inside the image (both vertically and horizontally).
In my Navigation bar I've put a bar button and I've set an image for the background. But the size of the image is much bigger than the size of the button. So I tried to tackle that as follow:
override func viewDidAppear(_ animated: Bool) {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 5, height: 5 ))
imageView.contentMode = .center
let image = UIImage(named: "Info")
imageView.image = image
info_btn.image = image
}
however the picture is still stretched out. Any idea how can I fix that?
Try info_btn.imageView.contentMode = .scaleAspectFit
I want to add image to right side of navigation bar. But I can't drag image view to navigation bar. I can only drag a button.
How can I do this ? I want same thing with whatsapp profile image. You know they have circle profile picture on right side of navigation bar.
Drag a UIView and drop it on the navigation bar first. Then drag a UIImageView into the view.
That should give you what you are looking for.
Set the image view height and width using constraints. You have to set the width and height of the containing view in the size inspector.
**This will Display the image on right side on navigation bar**
func viewDidLoad(){
let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageview = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
imageview.image = UIImage(named: "photo.jpg")
imageview.contentMode = UIViewContentMode.scaleAspectFit
imageview.layer.cornerRadius = 20
imageview.layer.masksToBounds = true
containView.addSubview(imageview)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
}
Don't know about storyboards. But from code you can create UIBarButtonItem with custom view.
let customView = UIImageView()
let customViewItem = UIBarButtonItem(customView: customView)
self.navigationItem.rightBarButtonItem = customViewItem
From storyboard:
Just drag UIBarButtonItem into your controller navigation bar. In element menu(Attribute inspector) select identifier: Custom and Image: the image you want.