Remove Text Horizontal Padding in SwiftUI - swift

There is extra horizontal padding for text in SwiftUI. This extra padding appears in some devices (canvas, simulator, and physical device) like iPhone 14 Pro, while it does not exist in other devices like iPhone 14 Pro Max (xCode Version 14.2). I'd like to have the text and H1 aligned leading. How can I remove the extra padding?
struct TestView: View {
var body: some View {
VStack {
HStack {
Text("H1")
.font(.custom("GillSans-bold", size: 18))
Spacer()
Image(systemName: "doc")
}
Text("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.")
.font(.custom("GillSans", size: 18))
.background(.cyan)
}
.background(.yellow)
.padding()
}
}

This is not a padding. The box of your text is aligned to the center by default and text can't be of exact with of the screen. So you probably want to do something like:
VStack (alignment: .leading) {
...
}
Also you could do the same trick that you did in the first line by adding Spacer in HStack which will push the text to the edge.

Related

Prevent Text with background from expanding to maxWidth

I have a Text with a background (which is just a coloured bubble), and I'm trying to restrict its maxWidth to some width, say 200 for this example.
I'm running into a problem with .frame(..., maxWidth: ...).
Expected Behvaiour
smaller messages are as big as they need to be
larger messages will have a width up to the maxWidth and will wrap without text being truncated
Actual Behaviour
no matter in which order the .background() and .frame() modifiers are applied, the Text is always drawn as what I have set the maxWidth to be.
In more detail:
Applying the .background() before the .frame(maxWidth: 200):
Text(...)
.fixedSize(horizontal: false, vertical: true)
.background(
Rectangle()
)
.frame(maxWidth: 200)
This results in the frame of all the Texts being of width 200 - which I'm not understanding. I understand that the background is applied before, so the background size is correct, but why does the frame become the maximum width? Am I misunderstanding how the maxWidth: frame works?
This behaviour is the exact same without the .fixedSize() modifier, as all that does is prevent the text from truncating into ...
Applying the .background() after the .frame(maxWidth: 200):
Text(...)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: 200)
.background(
Rectangle()
)
This shows the behaviour again, but this time with the background also affected by the frame. The modifier stacking behaviour is expected, but again, I'm not sure why the Text is expanding to the maxWidth.
For further information, these Texts are within an HStack with a Spacer() on the left side. The HStacks are within a larger VStack+ScrollView.
Removing the HStack, Spacer() or the ScrollView/VStack does not change this behaviour.
I have tried giving it a minWidth and applying other modifiers to no avail. Any help is appreciated, as I feel that I'm missing something very obvious!
Thanks.
Edit: greater concision.
You should apply .frame(maxWidth:) to the VStack, not to each Text.
struct TestView: View {
var body: some View {
VStack {
HStack {
MyText(text: "111")
MyText(text: "111")
}
MyText(text: "222222222")
MyText(text: "3333333333333")
MyText(text: "Long long long long long long long long long long text")
}
.frame(maxWidth: 200) // << there
}
struct MyText: View {
var text: String
var body: some View {
Text(text)
.padding(6)
.background(
Rectangle()
.fill(.yellow)
)
.border(.blue)
}
}
}
The blue border represents the actual Text frame sizes.
If you need a trailing alignment, then you should to set it in VStack(alignment: .trailing).
If you also need a trailing alignment inside Text frame, you can apply .multilineTextAlignment(.trailing) to Text.

How to center Text within a List?

I want to center Text in a List, I want to see the text in Text in the middle. I tried some solutions, but the text remains leading.
I was expecting this to work:
struct ContentView: View {
var body: some View {
List {
Text("Hello, world!")
.frame(alignment: .center)
}
}
}
The text remains leading. I also tried this, but not working:
.multilineTextAlignment(.center)
The only solution is this:
HStack(alignment: .center) {
Spacer()
Text("Hello, world!")
Spacer()
}
But I can not imagine this is the right way, I mean I add literally 3 views just to center a simple text. Is there a simpler solution? When I have a large text and I use .multilineTextAlignment(.center), the text is centered. I want it to work with 1 line of text.
You need to give all width for frame, otherwise it just fits text, so alignment has no effect.
This should work:
List {
Text("Hello, world!")
.frame(maxWidth: .infinity, alignment: .center)
}

SwiftUI truncate content of ForEach

In the following code snippet, the red circles obviously don't fit onto the screen and therefore only the trailing part of the HStack is shown. Instead of that behavior, I want the leading text of the HStack to always be visible and truncate the trailing red circles that don't fit into the available space (replaced with ...).
How can I do this?
struct ContentView: View {
var body: some View {
HStack {
Text("This text should always be visible")
.layoutPriority(1)
Spacer()
ForEach(0..<20) { index in
Image(systemName: "circle.fill")
.font(.body)
.foregroundColor(.red)
}
}
}
}
So I want this:
instead of this:
Can get everything except the ellipses with:
struct ContentView: View {
var body: some View {
HStack {
Text("This text should always be visible")
.fixedSize()
ForEach(0..<20) { index in
Image(systemName: "circle.fill")
.font(.body)
.foregroundColor(.red)
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
In order to get the ellipses you'll probably have to calculate frame sizes or try to get your images into an attributed string which could get pretty complicated. You might also consider a scroll view or grid.

SwiftUI - Change layout of card based on height of Text view

Based on the height of a Text-view, the Card is supposed to either show the complete text (if the input text is short) or display parts of the text and a "Continue reading"-button, which navigates to a Detail-view.
Unfortunately, the GeometryReader doesn't update it's height after the Text-view is rendered (or it does, but the Card-view is not rendered again). OnAppear the geo.size.height is 10, therefore the "Continue reading" code is never executed.
Code:
struct CardView: View {
var title: String = "Title"
var text: String = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
var body: some View {
ScrollView { // will be embedded in one on its parentView
GeometryReader { geo in
VStack {
Text(title)
.font(.title2)
Divider()
.padding(.top, -4)
if geo.size.height >= 420 {
VStack {
Text(text)
.frame(height: 340)
Divider()
.padding(.top, -4)
Button(action: {
//button for testing, NavigationLink later
}, label: {
HStack {
Text("Continue reading")
Spacer()
Image(systemName: "chevron.right")
}
.padding(.top, -16)
.frame(height: 44)
.contentShape(Rectangle())
})
.buttonStyle(PlainButtonStyle())
}
} else {
Text(text)
.padding(.bottom, 8)
.fixedSize(horizontal: false, vertical: true)
}
}
.cornerRadius(10)
.padding(.horizontal)
.padding(.top)
.overlay( RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.padding()
.onAppear{
print(geo.size.height)
}
}
}
}
}
struct CardView_Previews: PreviewProvider {
static var previews: some View {
CardView()
}
}
Adding a GeometryGetter to the .background{} of the Card-view did the trick. It updates the #State variable once the view has rendered, which can then be utilised in the if-statement to render the proper layout.
Pretty ugly, but does the job...
Code:
import SwiftUI
struct NewsCardView: View {
var title: String = "Lorem Ipsum is simply dummy text of the printing and typesetting industry"
var text: String = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."
#State private var rect: CGRect = CGRect()
var body: some View {
VStack {
Text(title)
.font(.title2)
.fixedSize(horizontal: false, vertical: true)
Divider()
.padding(.top, -4)
if rect.size.height >= 400 {
VStack {
Text(text)
.frame(maxHeight: 320)
Divider()
.padding(.top, -4)
NavigationLink(destination:
TestView()
, label: {
HStack {
Text("Continue reading")
Spacer()
Image(systemName: "chevron.right")
}
.padding(.top, -16)
.frame(height: 44)
.contentShape(Rectangle())
})
}
} else {
Text(text)
.padding(.bottom, 8)
.fixedSize(horizontal: false, vertical: true)
}
}
.cornerRadius(10)
.padding(.horizontal)
.padding(.top)
.overlay( RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.padding()
.background(GeometryGetter(rect: $rect))
}
}
struct NewsCardView_Previews: PreviewProvider {
static var previews: some View {
NewsCardView()
}
}
struct GeometryGetter: View {
#Binding var rect: CGRect
var body: some View {
return GeometryReader { geometry in
self.makeView(geometry: geometry)
}
}
func makeView(geometry: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.rect = geometry.frame(in: .global)
}
return Rectangle().fill(Color.clear)
}
}
``

SwiftUI mysterious spacing between large Text and TextField in VStack

I'm having trouble figuring out why there's some spacing below my text.
struct testView: View {
#State private var notes = ""
var body: some View {
VStack {
Text("Larg Text").font(.system(size: 70))
.background(Color.red)
TextField("Add a note", text: $notes)
.background(Color.red)
Spacer()
}
.background(Color.yellow)
}
}
For some reason, there's this mysterious space between Text and TextField. This space seems to decrease if I
Decrease the font size
Do not specify a font size
Don't use a TextField after Text view
Don't use a Text view before TextField
In other words, this font size–dependent spacing seems to only happen between Text and TextField. I'm utterly confused. I'd like to get rid of this space.
Appreciate your help!
It is default automatic spacing. The solution is to specify explicitly
VStack(spacing: 0) { // << here !!
Text("Larg Text").font(.system(size: 70))
.background(Color.red)
TextField("Add a note", text: $notes)
.background(Color.red)
Spacer()
}