So I have a LazyVGrid that I am utilizing inside my project, but I am having an issue in getting one of my fields to align properly.
So here is the code that I have:
VStack {
LazyVGrid(columns: [GridItem(), GridItem(), GridItem(alignment: .top)], content: {
Spacer()
Image("onboarding-logo")
.renderingMode(.original)
.resizable()
.scaledToFit()
.frame(width: 120.0)
.padding()
.clipped()
.frame(alignment: .center)
.border(.red)
NavigationLink(destination: MainView().environmentObject(mainViewModel), label: {
Text("Skip")
.frame(height: .infinity, alignment: .top)
.clipped()
.padding()
.foregroundColor(Color.white)
})
})
.border(.red)
}
This outputs the following view:
How could I make the "Skip" text be in the top-right corner? As shown here:
I would like to keep all the columns "top" aligned, "logo" center/top aligned, and the "Skip" button top-right aligned, is this possible?
SwiftUI has a number of combined alignment values such as .topLeading and .bottomTrailing.
In your case, you want your Text in the top right corner, which is .topTrailing for a left to right language.
Replace:
LazyVGrid(columns: [GridItem(), GridItem(), GridItem(alignment: .top)], content: {
with:
LazyVGrid(columns: [GridItem(), GridItem(), GridItem(alignment: .topTrailing)], content: {
Related
I have two Text views. They are placed next to each other horizontally. How can I align them such that the meeting point of the views is also the center of the container view, regardless of how long either string is?
For example...
This is the first string|Second string.
The pipe here would be the center of the container view. Obviously a simple HStack wouldn't work unless both strings were exactly the same width. (Side note: In my particular use case, the strings won't be so long that they'll need to truncate or line wrap, but that might be useful for other people who have this question).
You can use a .frame(maxWidth: .infinity) on both Texts, which will end up making them equal widths (50% of the parent).
struct ContentView : View {
var body: some View {
VStack {
HStack(spacing: 2) {
Text("Short")
.frame(maxWidth: .infinity, alignment: .trailing)
.border(Color.blue)
Text("Longer. This is longer text....")
.lineLimit(1) // remove/change this if you want it to accommodate multiple lines
.frame(maxWidth: .infinity, alignment: .leading)
.border(Color.green)
}
}
}
}
You can play with the alignment on each depending on your need. Of course, the borders are only there for debugging.
To ensure both sides of the pipe are equal width, you can use 2 Spacer()s.
HStack {
Spacer()
Divider()
Spacer()
}
Then, you can overlay text on top of each Spacer().
struct ContentView: View {
var body: some View {
HStack {
Spacer()
.overlay(
Text("Hi! This text is in multiple lines. Cool, right?")
.fixedSize(horizontal: false, vertical: true) /// allow text wrap
.multilineTextAlignment(.trailing) /// align text to right when there are multiple lines
.frame(maxWidth: .infinity, alignment: .trailing) /// align text to right when there's 1 line
)
Divider()
Spacer()
.overlay(
Text("Hello!")
.fixedSize(horizontal: false, vertical: true)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
)
}
}
}
Result:
I have a ZStack in which an Image is presented.
The image needs to be scaled in some cases.
I am failing on aligning the scaled image on the bottom of the ZStack, it is always presented in the middle.
I tried ZStack(alignment: .bottom) in combination with .alignmentGuide(.bottom) for the image, but this does not change the outcome.
Also putting a VStack around the image and placing a Spacer() above it does not change the result.
The HStack is not relevant and is only shown, because I need an ZStack in this construct. But The main issue is with the VStack, that it does not move after scaling in the Space of the ZStack.
It seems like .scaleEffect just uses position and frame of the original image and places the scaled image in the middle. Is this a limitation of scaleEffect? What other function can be used?
This is my View (reduced code): // I colored the background purple, to show the full size of the ZStack
var body: some View {
ZStack(alignment: .bottom) {
Color.purple
Image(battlingIndividual.getMonster().getStatusImageName(battlingIndividual.status))
.resizable()
.scaledToFill()
.scaleEffect(battlingIndividual.getMonster().size.scaleValue)
HStack() {
SkillViews(battlingIndividual: battlingIndividual)
Spacer()
}
}
}
The outcome is this:
But it should look like this:
EDIT: I added a Background to the image, in order to show that the image is centered in the ZStack.
Solution:
We donĀ“t need an alignment in this case, we need an anchor:
.scaleEffect(battlingIndividual.getMonster().size.scaleValue, anchor: .bottom)
Solution Image:
I figured it out.
.scaleEffect uses its own anchor, which can be set to .bottom.
scaleEffect(_:anchor:) Apple Developer
Therefore I needed only to add "ancor: .bottom" to the scaleEffect.
.scaleEffect(battlingIndividual.getMonster().size.scaleValue, anchor:
.bottom)
for the following result:
I assume this view container ZStack is a one cell view, so you need to align not ZStack which tights to content, but entire HStack containing those monster cells, like
HStack(alignment: .bottom) { // << here !!
ForEach ... {
MonsterCellView()
}
}
Please, put your Image in a VStack and a Spacer() above the image and your Images will be on the bottom of the Stack. The alignment .bottom is only to aline multiple views with each other, but you are not having multiple views in your Stack. The HStack doesn't count for the alignment.
If I try this out in my example and scale the image down,
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Color.purple
VStack {
Spacer()
Image(systemName: "ladybug")
.resizable()
.scaledToFit()
.frame(width: 100, height: 100, alignment:
HStack {
Image(systemName: "hare")
.resizable()
.frame(width: 50, height: 50)
Image(systemName: "hare")
.resizable()
.frame(width: 50, height: 50)
Image(systemName: "hare")
.resizable()
.frame(width: 50, height: 50)
Spacer()
}
}
}
}
}
I get this.
Kind regards,
MacUserT
I am trying to right-align several overlays but am unable to figure out how to do this.
What I have here is a VStack of 2 images on the right side of the screen, and I want to display an overlay text label for each image, to the left of the image, but right-aligned with the other labels, like so:
A LABEL A
ANOTHER LABEL B
The code below displays the labels center aligned, like so:
A LABEL A
ANOTHER LABEL B
struct TestOverlayOffset : View {
var body : some View {
HStack {
Spacer()
VStack(spacing: 32) {
Spacer()
Image(systemName: "a.circle").font(.title)
.overlay(labelOnTheLeft("A LABEL"))
Image(systemName: "b.circle").font(.title)
.overlay(labelOnTheLeft("ANOTHER LABEL"))
Spacer()
}
.background(Color.gray)
}
.background(Color.green)
}
func labelOnTheLeft(_ text: String) -> some View {
GeometryReader { proxy in
Text(text)
.fixedSize()
.foregroundColor(.black)
.background(Color.yellow)
.offset(x: -128 - proxy.size.width/2)
}
}
}
Here is possible solution (with smallest changes and removed hardcoding).
Tested with Xcode 12.1 / iOS 14.1
func labelOnTheLeft(_ text: String) -> some View {
GeometryReader { proxy in
Text(text)
.fixedSize()
.foregroundColor(.black)
.background(Color.yellow)
.padding(.trailing)
.offset(x: -proxy.size.width)
.frame(width: proxy.size.width, alignment: .trailing)
}
}
I can't seem to get the Spacer() function to work when I'm within an HStack and trying to create space between my Text and Textfield views. The Spacer works to space out other areas of the view, but whenever I attempt to space between these two elements, it doesn't work.
Here is the code I'm working with:
VStack(alignment: .leading, spacing: 0) {
Rectangle()
.frame(height: 2, alignment: .top)
.foregroundColor(Color("grey2"))
Text("Tickets")
.kerning(0.5)
.scaledFont(name: "Gotham Medium", size: 18)
.foregroundColor(Color("grey4"))
.padding(.horizontal)
.padding(.bottom, 3)
.padding(.top, 35)
Rectangle()
.frame(height: 2, alignment: .top)
.foregroundColor(Color("grey2"))
HStack {
Text("Ticket URL")
.kerning(0.5)
.scaledFont(name: "Gotham Book", size: 16)
.foregroundColor(Color("spaceblack"))
Spacer()
TextField("Enter URL", text: $url)
}
.background(Color.blue)
.padding()
Rectangle()
.frame(height: 2, alignment: .top)
.foregroundColor(Color("grey2"))
}
.frame(maxWidth: .infinity)
.background(Color.red)
try adding .fixedSize() modifier to your TextField
TextField("Enter URL", text: $url)
.fixedSize()
or set a frame like so
TextField("Enter URL", text: $url)
.frame(width:200, height:50, alignment:.leading)
The problem is that TextField and Spacer() would take all available space and in this case TextField gets the priority; However, if you specify a fixed size or a frame to it then TextField won't stretch to take full space instead it will be fixed.
.fixedSize would allow your TextField to start small but eventually it will keep stretching the more text you write which can cause unwanted behavior.
.frame will fix your size to the provided width and hence there won't be any stretch and Spacer will have priority to take available space.
Added on the 24th of July:
This line of code fixes the space in the detail view. However... in the list view the title has become a lot smaller too.
.navigationBarTitle(Text("Egg management"), displayMode: .inline)
Added on the 23th of July:
Thanks to the tips I made a lot of progress. Especially the tip to add borders does wonders. You see exactly what happens!
However, there seems to be a difference between the Xcode Preview canvas, the simulator and the physical device. Is this a bug because -after all- it is still beta? Or is there anything I can do?
As you can see in the images... only in the Xcode Preview canvas the view connects to the top of the screen.
I believe it has something to do with the tabbar. Since when I look at the Xcode Preview canvas with the tabbar... that space above is also there. Any idea how to get rid of that?
Original postings:
This is my code for a detailed list view:
import SwiftUI
struct ContentDetail : View {
#State var photo = true
var text = "Een kip ..."
var imageList = "Dag-3"
var day = "3.circle"
var date = "9 augustus 2019"
var imageDetail = "Day-3"
var weight = "35.48"
var body: some View {
VStack (alignment: .center, spacing: 10) {
Text(date)
.font(.title)
.fontWeight(.medium)
ZStack (alignment: .topLeading){
Image(photo ? imageDetail : imageList)
.resizable()
.aspectRatio(contentMode: .fit)
.background(Color.black)
.padding(.trailing, 0)
.tapAction {
self.photo.toggle() }
HStack {
Image(systemName: day)
.resizable()
.padding(.leading, 10)
.padding(.top, 10)
.frame(width: 40, height: 32)
.foregroundColor(.white)
Spacer()
Image(systemName: photo ? "photo" : "pencil.circle")
.resizable()
.padding(.trailing, 10)
.padding(.top, 10)
.frame(width: 32, height: 32)
.foregroundColor(.white)
}
}
Text(text)
.lineLimit(6)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 6)
} .padding(20)
}
}
#if DEBUG
struct ContentDetail_Previews : PreviewProvider {
static var previews: some View {
ContentDetail()
}
}
#endif
Also included is the preview canvas. What I don't get is how I can make sure the text and photo are aligned to the top (instead of the middle). I tried with Spacers, padding etc.
I must be overseeing something small I guess... but. Can somebody point me in the right direction? Thanks.
Added:
After both answers I added a Spacer() after the last text. In Xcode in the preview canvas everything looks okay now. But on my connected iPhone 7 Plus there are some problems: the view is not aligned to the top, and the image is cropped (icon on the right is gone; white banding to the right).
Adding a Spacer() after the last text shifts everything to the top. Tested on iPhone Xr simulator (not preview).
...
Text(text)
.lineLimit(6)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 6)
Spacer()
}
To remove the space at the top:
VStack {
...
}
.padding(20)
.navigationBarTitle("TITLE", displayMode: .inline)
Think in terms of what a Spacer() does. It "moves" the views as far apart as it can - at least, without a specific space.
So you have this:
VStack {
Text
ZStack {
Image
HStack {
Image
Spacer()
Image
}
}
Text
}
All told, going from inner to outer, you have a horizontal stack of two images placed as far apart (the spacer is between them) inside of a "Z axis" stack that places an image on top of them, inside of a vertical stack that has some text above it.
So if you want to move everything in that vertical stack to the top, you simply need to add one last spacer:
VStack {
Text
ZStack {
Image
HStack {
Image
Spacer()
Image
}
}
Text
Spacer() // <-- ADD THIS
}
Last note: Don't be afraid to adding additional "stacks" to your view. In terms of memory footprint, it's really just a single view with no performance hit.
EDIT: I took your original view and changed everything to placeholders...
var body: some View {
VStack (alignment: .center, spacing: 10) {
Text("Text #1")
.font(.title)
.fontWeight(.medium)
ZStack (alignment: .topLeading) {
Text( "Image #1")
HStack {
Text("Image #2")
Spacer()
Text("Image #3")
}
}
Text("Text #2")
.lineLimit(6)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 6)
} .padding(20)
}
As expected, everything is vertically centered. Adding a Spacer() below "Text #2" throws everything to the top. A couple of thoughts:
Starting there, and add in your Image views one by one. Add in the modifiers like that also.
I don't have the specific images you are rendering, so maybe put a noticeable background color on various things (orange is my personal favorite) and see if the top Image is actually on top but the image makes it appear as though it isn't. A border would work pretty well too.