SwiftUI generates unwanted space around an Image - swiftui

The following code produces a simple VStack with Text views that show no spacing in-between them (rows 1 & 2).
However, adding an image to the 3rd row (green) adds unwanted spacing above and below the entire row.
struct ContentView: View {
var body: some View {
VStack {
HStack {
Text("one thing")
}.background(Color(.yellow))
HStack {
Text("nothing")
}.background(Color(.red))
HStack {
Text("three")
Image(systemName: "star")
.resizable()
.frame(width: 8, height: 8)
}.background(Color(.green))
HStack {
Text("three things")
}.background(Color(.red))
}
}
}
How can I avoid the additional unwanted space?
The space shows independent of image size (even with an image just a few pixels in dimension).
And, of course, I'd like to know why the space is generated.
Thanks for any help
Screenshot of above code:

You may adjust the spacing of VStack:
var body: some View {
VStack (spacing: 0) {
HStack {
Text("one thing")
}.background(Color(.yellow))
HStack {
Text("nothing")
}.background(Color(.red))
HStack {
Text("three")
Image(systemName: "star")
.resizable()
.frame(width: 8, height: 8)
}.background(Color(.green))
HStack {
Text("three things")
}.background(Color(.red))
}
}

Related

Utilize HStack to confirm to logo and skip text

Does anyone know how to make the following view in SwiftUI?
HStack:
[ blank logo(centered) Skip(text)]
So I have the following HStack:
Zstack(alignment: .topLeading) {
VStack{
HStack {
Image("onboarding-logo")
.resizable()
.scaledToFit()
.frame(width: 150.0, height: 150.0)
.padding(.top, 35)
}
}
}
Does anyone know how I can have a "Skip" text in the top right corner of the screen, but also keep my logo centered and not have anything on the left side? I've tried Spacers and all, but I'm having no luck.
I would like to click "Skip" and then lead to another view.
There are many ways to achieve this. Here I have used LazyVGrid since other answers based on Stacks
struct ContentView: View {
var body: some View {
VStack {
LazyVGrid(columns: [GridItem(), GridItem(), GridItem()], content: {
Spacer()
Image(systemName: "star.fill")
.frame(width: 150, height: 150, alignment: .center)
.border(.gray)
Button(action: {
}, label: {
Text("Skip")
})
})
.border(.gray)
Spacer()
}
}
}
Is this the screen configuration you want?
The Zstack is used to center the Hstack into which the image is placed, and the new HStack uses a spacer to move the Text to the right.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
VStack {
HStack(alignment: .center) {
Image("farnsworth")
.resizable()
.scaledToFit()
.frame(width: 150.0, height: 150.0)
}
Spacer()
}
VStack {
HStack {
Spacer()
Button {
// do Something...
} label: {
Text("Skip>>")
.padding(.top, 10)
}
}
Spacer()
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Why do the views extend wider than the screen?

Edit: Substitute your "system name:" of choice. "pencil.circle" works fine. "edit" is not a valid SF Symbol.
(I've simplified my code so you can cut and paste. That's why you see .frame, resizable, etc. where much simpler code might your first instinct.)
I have created a view which is a vertical list of row items (table view).
Each row item has a horizontal view with two images inside it.
The images take up too much space and do not fit correctly on the screen:
import SwiftUI
#main
struct StackOverflowDemoApp: App {
var body: some Scene {
WindowGroup {
TandemView()
}
}
}
struct PaddedImageView: View {
let color: Color = .red
var body: some View {
ZStack {
color
Image(systemName: "edit")
.resizable()
.padding()
}
Spacer()
}
}
struct TandemView: View {
var body: some View {
HStack {
Spacer()
Image(systemName: "pencil")
.resizable()
.background(Color.orange)
.frame(height: 80)
.aspectRatio(1, contentMode: .fill)
PaddedImageView()
.frame(width: 200, height: 80)
}
.padding()
.fixedSize()
}
}
struct TandemView_Previews: PreviewProvider {
static var previews: some View {
TandemView()
}
}
The above is the closest I can get to the desired layout (it just needs to fit horizontally). I experimented with GeometryReader but that did not produce desired results.
Here are some things I tried:
The code as provided
NoConstraintsOnPencilOrHStack
NoConstraintsOnTandemView
NoConstraintsOnImageInPaddedViewButWithFrameConstraint
I am trying to get a row view which consists of two Images (my actual source consists of UIImage objects) that fits within the width of the screen.
Edit:
After Accepting cedricbahirwe's spot-on response, I was able to simplify the code further. New results:
I added at the top level
TandemView()
.padding(.horizontal)
I removed:
// Spacer()
at the end of PaddedImageView
updated TandemView -- changed both frames and removed 3 lines:
struct TandemView: View {
var body: some View {
HStack {
Spacer()
Image(systemName: "pencil")
.resizable()
.background(Color.orange)
.frame(width: 80, height: 80)
// .aspectRatio(1, contentMode: .fill)
PaddedImageView()
.frame(height: 80)
}
// .padding()
// .fixedSize()
}
}
This is happening because of the layout of PaddedImageView View, you can actually remove the Spacer since it is not needed there.
So change
struct PaddedImageView: View {
let color: Color = .red
var body: some View {
ZStack {
color
Image(systemName: "edit")
.resizable()
.padding()
}
Spacer()
}
}
to
struct PaddedImageView: View {
let color: Color = .red
var body: some View {
ZStack {
color
Image(systemName: "edit")
.resizable()
.padding()
}
}
}
Note:
SwiftUI Engine infers the layout of your view from the implementation of the body property. It's recommended to have one Parent View inside the body property.

Automatic adjust font size in Text() of Swiftui?

Is there anyway to automatically adjust the font size according to the length of the text to be displayed? For example, by setting the frame size. Can it reduce the font size automatically so that the width for displaying the font still the same?
var body: some View {
VStack {
ZStack {
GeometryReader { g in
ZStack {
ScrollView(.vertical, showsIndicators: true, content: {
VStack {
ZStack {
VStack {
Image("cat")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width:g.size.width, height:g.size.height/4*3)
.clipped(antialiased:true)
.shadow(radius:25, x:0, y:0)
VStack (){
HStack {
VStack (alignment: .leading) {
HStack {
Text("XXXX").font(.title)
.font(.system(size:35)).foregroundColor(.black).shadow(radius: 5)
})
Spacer()
}
HStack {
Text("xxxxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxx ").font(.title2) // very long text here
Spacer()
}
}
}
}
.padding()
.frame(width:g.size.width, height:g.size.height/4*1, alignment: .top)
}
}.frame(width: g.size.width, height: g.size.height, alignment: .top)
}
.edgesIgnoringSafeArea(.all)
}
}
}
As see in my code, there is a very long text there. The text can be displayed perfectly on most of the device except iPod Touch that it will cause "xxxxxxxx xxxxxxxx xxxx ... ", "..." at the back because it is too long to be displayed. Is there anyway that it automatically, either jump to the next line, or shrink the fonts so that there will not be "..." at the back?
Use minimumScaleFactor(_:) https://developer.apple.com/documentation/swiftui/text/minimumscalefactor(_:)
struct ContentView: View {
var body: some View {
VStack{
Text("Test string")
.minimumScaleFactor(0.1) //<--Here
.frame(width: 15, height: 15)
}
}
}

Why the frame of an SF Symbol isn't encompassing its entire image?

When I put an image on screen, I expect the image to be entirely enclosed in its own frame, but apparently I am wrong. Does anyone know why? Thanks.
My Code:
struct ContentView: View {
var body: some View {
HStack {
Group {
Image(systemName: "envelope.badge.fill")
Image(systemName: "moon.stars.fill")
} // Group
.foregroundColor(.yellow)
.font(.system(size: 128))
.border(Color.red)
} // VStack
.padding(40)
.background(Color.white)
}
}
Result:
This appears to be a bug that is now fixed in SwiftUI 2.0 (Xcode 12).
Here is a workaround for SwiftUI 1.0 (Xcode 11). It uses UIImage(systemName:) to get the actual dimensions of the image, and then uses .aspectRatio(_:contentMode:) to set the correct aspect ratio.
struct SystemImage: View {
let name: String
var body: some View {
let uiImage = UIImage(systemName: name)!
return Image(systemName: name)
.resizable()
.aspectRatio(uiImage.size, contentMode: .fit)
}
}
struct ContentView: View {
var body: some View {
HStack {
Group {
SystemImage(name: "envelope.badge.fill")
SystemImage(name: "moon.stars.fill")
} // Group
.foregroundColor(.yellow)
.font(.system(size: 128))
.border(Color.red)
} // VStack
.padding(40)
.background(Color.white)
}
}
Try placing the padding before the border.
edit...
Does this help:
HStack {
Group {
Image(systemName: "envelope.badge.fill")
Image(systemName: "moon.stars.fill")
} // Group
.foregroundColor(.yellow)
.font(.system(size: 128))
.frame(width:150, height: 150, alignment: .center)
.padding(10)
.border(Color.red)
} // VStack
.padding(40)
.background(Color.white)

How to fix increased height in NavigationView List row with Images in SwiftUI"

I'm using Xcode 11 beta 5 and iPhone SE with iOS 13 beta 5.
In my app I have a NavigationView where each List row contains of two lines. I now want to add an Image(systemName: "circle") at the end of the text of the 2nd text line. When doing that, the space between the first and second text line increases.
I tried with adding using NSTextAttachment() as described in the WWDC19 session on SF Symbols, but the image isn't shown at all. See Section 1 in the code and screenshot.
I also tried using using Image(uiImage: UIImage(systemName: "circle")!), the image is shown but the space between the lines increased. See Section 2.
Also, for reference, when just using text, the line spacing as shown in Section 3 is what I want to have.
The code I used is:
import SwiftUI
struct ContentView: View {
func appendSymbol(string: String /*, image:UIImage*/ )-> String {
let mutableString = NSMutableAttributedString(string: string)
let circleImage = UIImage(systemName: "circle")
let redCircleImage = circleImage?.withTintColor(.red, renderingMode: .alwaysOriginal)
let circleAttachment = NSTextAttachment(image: redCircleImage!)
let circleString = NSAttributedString(attachment: circleAttachment)
mutableString.insert(circleString, at: 3)
return mutableString.string
}
var body: some View {
NavigationView {
List {
Section(header: Text("Section 1")) {
HStack {
VStack (alignment: .leading) {
HStack {
Text("Text 1.1")
}
.border(Color.red)
HStack {
Text(appendSymbol(string: "Tex t 1.2"))
}
.border(Color.green)
}
.border(Color.red)
}
.border(Color.red)
}
Section(header: Text("Section 2")) {
HStack {
VStack (alignment: .leading) {
HStack {
Text("Text 2.1")
}
.border(Color.red)
HStack {
Text("Text 2.2")
Image(uiImage: UIImage(systemName: "circle")!)
}
.border(Color.green)
}
.border(Color.red)
}
.border(Color.red)
}
Section(header: Text("Section 3")) {
HStack {
VStack (alignment: .leading) {
HStack {
Text("Text 3.1")
}
.border(Color.red)
HStack {
Text("Text 3.1")
Text("circle")
}
.border(Color.red)
}
.border(Color.red)
}
}
}
}
}
}
Screenshot using the included code
Any idea why the first code (Section 1) isn't working, and how to fix the spacing?
Simply try adding spacing after the alignment option of your VStack.
Example code for Section 2 would be like this:
Section(header: Text("Section 2")) {
HStack {
//adding the spacing option for the VStack
VStack (alignment: .leading, spacing: 0) {
HStack {
Text("Text 2.1")
}
.border(Color.red)
HStack {
Text("Text 2.2")
Image(uiImage: UIImage(systemName: "circle")!)
}
.border(Color.green)
}
.border(Color.red)
}
.border(Color.red)
}
You could also use
Image(systemName: "circle")
instead of
Image(uiImage: UIImage(systemName: "circle")!)