9

I have this simple SwiftUI code. I want all symbols to be aligned centered, just like the cloud symbol.

struct ContentView : View {
var body: some View {
    HStack(alignment: .center, spacing: 10.0) {
        Image(systemName: "cloud.sun")
        Image(systemName: "cloud")
        Image(systemName: "cloud.bolt")
        Text("Text")
        }.font(.title)
    }
}

But as you can see below, the first and the last symbol are not centered. Am I missing something, or is this a bug?

Centered HStack

Cheers!

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
Daniel
  • 1,473
  • 3
  • 33
  • 63

2 Answers2

8

This is what it's going on.

enter image description here

The Image views are not resizing.

It looks like they're not aware of their intrinsic content size, or maybe it reports the wrong value.

To fix it:

struct ContentView : View {
    var body: some View {
        HStack(alignment: .center, spacing: 10.0) {
            Image(systemName: "cloud.sun")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .background(Color.red)
            Image(systemName: "cloud")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .background(Color.yellow)
            Image(systemName: "cloud.bolt")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .background(Color.pink)
            Text("Text").background(Color.green)
        }
        .frame(width: 250, height: 50)
        .background(Color.gray)
        .font(.title)

    }
}

...make the Images resizable, and also make sure the aspect ratio is set to .fit, or they will stretch.

Set also frame on the HStack or it will expand to fill the whole screen.

@MartinR suggested an even better solution - creating the images via UIImage - see his comment below.

struct ContentView : View {

    var body: some View {
        HStack {
            Image(uiImage: UIImage(systemName: "cloud.sun")!)
                .background(Color.red)
            Image(uiImage: UIImage(systemName: "cloud")!)
                .background(Color.yellow)
            Image(uiImage: UIImage(systemName: "cloud.bolt")!)
                .background(Color.pink)
            Text("Text").background(Color.green)
        }
        .background(Color.gray)
        .font(.title)

    }

}

Output:

enter image description here

Matteo Pacini
  • 21,796
  • 7
  • 67
  • 74
  • 2
    I thought the SF symbols had a “natural baseline offset” for proper alignment with text. Therefore I would have expected something like `HStack(alignment: .lastTextBaseline)` to work – but it doesn't. – Martin R Jun 13 '19 at 13:05
  • 1
    @MartinR if feels like a bug to be honest - it should work exactly as you said – Matteo Pacini Jun 13 '19 at 13:14
  • 3
    If you go via UIImage, e.g. `Image(uiImage: UIImage(systemName: "cloud")!)` then the alignment seems to work,  but the icons are scaled differently. – Martin R Jun 13 '19 at 14:09
  • @MartinR I believe it still looks better than my solution - also, with yours there's no need to set a frame on the view, as it won't stretch to fill the superview. – Matteo Pacini Jun 13 '19 at 14:22
  • 1
    I agree that it feels like a bug - setting a mental note to test `HStack(alignment:)` in future betas. –  Jun 13 '19 at 17:04
  • 2
    Thanks. I filed a bug with the new Feedback-Assistent. Jay. :) – Daniel Jun 13 '19 at 19:05
  • I filed a bug: FB7158085: Alignment of Image SF Symbols is Random – Paul Solt Aug 29 '19 at 21:09
  • 1
    You'll need to use a templated image if you want to change the color. Image(uiImage: UIImage(systemName:"star.fill")!.withRenderingMode(.alwaysTemplate)) .foregroundColor(.white) – Paul Solt Aug 29 '19 at 21:10
  • @MatteoPacini this doesnt work with multline text tho, if i do this in the new line where the text ends there is huge right padding then the image to the right – Di Nerd Apps Jul 01 '20 at 09:34
  • 1
    This seems to be fixed in iOS 14, but remains a problem in iOS 13. – Andres Riofrio Jul 31 '20 at 03:35
  • @PaulSolt Any updates on the bug you filed? This doesn't seem to be solved on iOS 13.5.1 yet, but it is solved in the iOS 14 simulator of Xcode beta 3. Is there any good workaround? I've found trying to get the exact same size using the Image(uiImage:) hack to be near-impossible. – Andres Riofrio Jul 31 '20 at 04:27
  • Please duplicate the bug report for iOS 13. I have no updates: Recent Similar Reports: None Resolution: Open – Paul Solt Sep 17 '20 at 18:50
0

I came across the same problem as you: SF Symbols are not reporting the correct content size on iOS 13. (Though, it is fixed on iOS 14 and above.)

The problem with using UIImage as proposed in Matteo Pacinis solution is the poor compatibility with the SwiftUI dynamics: foreground color and font size (and dynamic type!) are not simply taken from the current SwiftUI context but have to be duplicated into the UIImage configuration (using UIImage(systemName:, withConfiguration:)) wich is often not practical. (see hackingwithswift.com for how to use this).

Looking at the problem above I propose the following solution:

HStack(alignment: .center, spacing: 10.0) {
    Image(systemName: "cloud.sun")
        .hidden()
        .overlay(
            Image(systemName: "cloud.sun")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .background(Color.red)
        )
    Image(systemName: "cloud")
        .hidden()
        .overlay(
            Image(systemName: "cloud")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .background(Color.yellow)
        )
    Image(systemName: "cloud.bolt")
        .hidden()
        .overlay(
            Image(systemName: "cloud.bolt")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .background(Color.pink)
        )
    Text("Text")
        .background(Color.green)
}
.background(Color.gray)
.font(.title)

It looks like a lot of code duplication but it has the advantage, that the symbols scale according to your font, respect the foregroundColor modifier and align according to your desired alignment

Output:

enter image description here

But there is still some issue with this approach: the image still has not the correct intrinsic size, the symbols are simply drawn "nicely centered". This means that the height of the HStack still depends of the height of the Text element. If you simply want to draw the symbols in .largeTitle font and the text in .body font, the result would look like below possibly causing overlapping with neighbouring views:

SF symbols aligned in HStack but drawing over the frame

I will still investigate further to find a solution ensuring the correct view size, as this really annoys me.

pd95
  • 1,999
  • 1
  • 20
  • 33