I am trying to make something similar to the mail app. I want to remove the spaces between all the rectangles. How do i do that?
I have tried using fixedSize() function but that doesn't seem to work for me.
ScrollView has an implicit VStack of sorts but you can't control its spacing afaik.
Try wrapping your content inside a VStack(spacing: 0).
You'll probably want to then add some padding after your ZStack.
You also shouldn't need to specify the width with UIScreen.main.bounds.width. If it isn't filling the available space you could use maxWidth: .infinity.
Related
I need to add a view on top of this chat bubble view, like an Instagram emoji reaction. Let's call it EmojiView. It will need to always be in the bottom left or right corner of the chat bubble. But the chat bubble's size will vary with how much text is in it. So I need to position EmojiView based on the size of the bubble it is on top of.
But there is no clear way for me to read the size of that bubble. GeometryReader takes up all available space, which is a problem in this situation. If I wrap the text inside a GeometryReader, it takes all the height space and I have no reliable height space to read from. If I modify the GeometryReader with .aspectRatio(.fit), the height just becomes equal to the width. If I set anything anywhere to .fixedSize() , the chat bubble no longer adapts to the text size. So please share a reliable way to read this geometry
Please help with this issue
Without GeometryReader, I can't read the geometry
With the GeometryReader, I cannot get a reliable height reading
There is no need to use GeometryReader.
Use ZStack and align the EmojiView on the bottomTrailing or bottomLeading depending on which bottom side you want to display it.
ZStack(alignment: .bottomLeading) {
HStack {
Text("I need to add a view on top of this chat bubble view, like an Instagram emoji reaction. Let's call it EmojiView. It will need to always be in the bottom left or right corner of the chat bubble. But the chat bubble's size will vary with how much text is in it. So I need to position EmojiView based on the size of the bubble it is on top of.")
.background(Color.secondary)
.padding()
}
Text("RABBITS")
.padding(8)
.background(Color.blue)
.padding(20)
}
What is happening now with constraints in SwiftUI? Do View types adapt automatically for bigger devices etc. or what should we have to do instead?
RIP, Constraints!
SwiftUI doesn't use layout constraints. UIKit is still around, it's not deprecated and fully functional, so if you continue to use the classic approach, you can use as many constraints as you wish.
However, if you choose to go with SwiftUI → rest in peace, constraints!
The core concept to align views with each other is using stacks:
HStack
VStack
If you want to overlay views (i.e. put one view on top of another), you can use a
ZStack
The View protocol itself (to which all view types mysteriously conform) has tons of functions called modifiers that you can use to customize your view's layout.
Examples
Here are some examples how you can achieve specific layouts with those modifiers compared to using constraints:
1. Aspect Ratio
Instead of
view.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: 2)
in UIKit you would write
view
.aspectRatio(2, contentMode: .fit)
in SwiftUI.
2. Spacing Between Views
Instead of
view2.leadingAnchor.constraint(equalTo: view1.leadingAnchor, constant: 8)
in UIKit you could arrange the views in a horizontal stack and add a spacer between them and add the frame modifier to specify its width:
HStack {
view1
Spacer()
.frame(width: 30)
view2
}
3. Equal Widths
This is where it gets more complicated. You can no longer specify that two views have an equal width. If they are in the same vertical stack (i.e. aligned in a vertical line), fine: just set the contentMode to .fill and control the actual width by setting the stack view's width → mission accomplished. ✅ But if they are not (for example, when they are in a horizontal stack), you have to find other ways to express that. The actual implementation will depend on the concrete layout you're trying to describe.
The general idea of SwiftUI is to keep views as small as possible and compose them. There's a little trade-off here: You pay the price that "constraints" between views in different view hierarchies get a lot more verbose to implement, the ultimate gain is that the layout is declarative and the code to create the most common user interfaces is dramatically simplified.
Screen Adaptation / Responsiveness
Custom views fill the entire available space by default, which means that the top most view automatically fills the entire screen – regardless of the actual screen size. You can use modifiers to change that behavior.
It still has constraint, in WWDC examples we saw HStack and VStack, which seems like UIStackView, so my guess it just clips to edges. You can still add padding to views so if you want constraint a UILabel (Text) 10pt to left you do something like this:
Text("Hello World").padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 0))
Constraints are very well gone, which is great, but there is something called .padding(), which can do some sort of constraint look by putting it on the left side with something like leading parameters which make an image go to the side of a view
struct ContentView: View {
var body: some View {
Image("x")
.padding(.leading)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I have 2 views in a horizontal UIStackView. What I'm trying to achieve is for the stack view to stay in horizontal mode if possible, but if this isn't possible then change to a vertical mode. Demonstrated in the image below. When in the second case it can't fit horizontally it changes to vertical:
How could this be achieved using UIStackViews and autolayout?
In a more general sense I would love to have a view that mimics CSS flex-wrap properties. I.e.:
Though that seems like a far more complicated task.
Is there a way to use different values for the spacing between views within a UIStackView? As I understand it, the spacing property is used for all views (or rather the room between them). I was hoping to just set the top space constraint to some value for each view but this seems to be ignored. Any suggestion? Thanks!
It looks like there is no build in solution for this. So we went with spacer views. We just added plain UIViews with our desired heights as elements of the UIStackView. It's not beautiful but it does the trick.
I am making a form based UIScrollView, which will contain some labels and text fields.
My ScrollView Height will increase as per the iOS device height.
PS: I do not want to add constraint to each and every element of the Scrollview, because in my case there could be 100 form fields.
What I want is, the inner content to fully occupy my scrollView like this:
Till now there a are no special constraints, the button is tagged with the bottom edge and the scroll view is pinned from the top edge. Also, the vertical spacing between scrollview and button is defined.
This is the autolayout constraint screenshot.
If the number of labels is variable, I recommend doing them in code, rather than in Interface Builder.
In code, you can use a loop to set every label to have the same width/height as the one above it. You may want to set their height to be >= a minimum value. Be sure to anchor the first label with the top, and the last label with the bottom.
But this can be cumbersome, why not just use a UITableView? you may modify the row height to let the cell fully occupy the view.
- tableView:heightForRowAtIndexPath:
- tableView:estimatedHeightForRowAtIndexPath:
I just face the same issue and already write a pod called TLFormView that do exactly that: a form based on UIScrollView.
It also has some nice features like:
declarative form taxonomy with TLFormModel Just extend it, add the properties you want and that's it. No delegate, no event handling, no boilerplate.
a nice way to handle different layouts for iPhone and iPad
conditional visibility with an NSPredicate that can access all the values in the other fields (e.g: show field A when field B has certain value)
in place help with a popover
all the fields are TLFormFields that extend UIView so you can place whatever you need.
You can try it right from the command line with pod try TLFormView.
If you want to know more I wrote some blog post about it here.
Please let me know your thoughts about it here or as a comment in the blog posts. Also any contribution is extremely welcome in the GitHub repo