0

I would like to use SF Symbols in a SegmentedControl in SwiftUI. Copy this code into a project and watch what happens. There is no good way to explain the behavior. You have to see it for yourself.

import SwiftUI

struct ContentView : View {
    @State private var favoriteColor = 0

    var body: some View {
        VStack {
            SegmentedControl(selection: $favoriteColor) {
                Image(systemName: "hammer.fill").tag(0)
                Image(systemName: "house.fill").tag(1)
                Image(systemName: "desktopcomputer").tag(2)
                Image(systemName: "cart.fill").tag(3)
                Image(systemName: "phone.arrow.right.fill").tag(4)
                Image(systemName: "wand.and.rays").tag(5)
                Image(systemName: "slider.horizontal.3").tag(6)
            }
            Text("Value: \(favoriteColor)")
        }
    }
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

If anyone figures this out please provide an answer or an explanation.

marc-medley
  • 8,931
  • 5
  • 60
  • 66
K. Law
  • 2,541
  • 3
  • 18
  • 27

1 Answers1

3

Update

In beta 5 the problem has gone away! Workaround no longer necessary. Also, SegmentedControl has been replaced by Picker:

Picker(selection: $selectedSegment, label: EmptyView()) {
    Image(systemName: "hammer.fill").tag(0)
    Image(systemName: "house.fill").tag(1)
    Image(systemName: "desktopcomputer").tag(2)
    Image(systemName: "cart.fill").tag(3)
    Image(systemName: "photo").tag(4)
    Image(systemName: "wand.and.rays").tag(5)
    Image(systemName: "slider.horizontal.3").tag(6)
}.pickerStyle(SegmentedPickerStyle())

Workaround Beta 4 and previous versions

Yeap, it's a bug. Until it gets fixed, here's a workaround. You basically encapsulate the segment in a separate view, and the problem goes away:

struct TabItem: View {
    let image: String
    let tag: Int

    var body: some View {
        Image(systemName: image).tag(tag)
    }
}

struct ContentView : View {
    @State private var favoriteColor = 0

    var body: some View {
        VStack {
            SegmentedControl(selection: $favoriteColor) {
                TabItem(image: "hammer.fill", tag: 0)
                TabItem(image: "house.fill", tag: 1)
                TabItem(image: "desktopcomputer", tag: 2)
                TabItem(image: "cart.fill", tag: 3)
                TabItem(image: "phone.arrow.right.fill", tag: 4)
                TabItem(image: "wand.and.rays", tag: 5)
                TabItem(image: "slider.horizontal.3", tag: 6)
            }
            Text("Value: \(favoriteColor)")
        }
    }
}
kontiki
  • 37,663
  • 13
  • 111
  • 125
  • This workaround worked. However, Xcode 11 beta 4 only partially fixed the problem. While you can use images in segmented controllers, there is still an issue. When the device changes orientation ei., goes into landscape mode, the segmented controller view expands and fills the width of the screen with extra non-functioning segments. I hope this issue is resolved because I have to limit the app to portrait view only. – K. Law Jul 21 '19 at 19:54
  • Hi @K.Law I am trying to reproduce the problem you mention when changing the device orientation, but I am failing. As far as I used the workaround I posted, the segments work fine. Any insight? What device are you using? Have any more code to share? – kontiki Jul 22 '19 at 10:38
  • 2
    Updated answer, as it seems beta 5 fixed the problem. – kontiki Jul 30 '19 at 09:31
  • @kontiki Do you know a way in beta 5 to make a segmented picker with images that come in an array? I tried many variants of ForEach, but they fail because Image is not Hashable, and neither \self nor \tag are accepted for the id parameter in ForEach. – pommy Aug 15 '19 at 07:24
  • 1
    @pommy Yes, it can be done. The answer depends on wether the array will have a constant number of images, or if they may change over time. If it is the first one, it is easier: `ForEach(array.indices) { idx in self.array[idx].tag(idx) }`. If not, it needs a little more code. Post a question and I'll answer there. – kontiki Aug 15 '19 at 07:45
  • @kontiki Brilliant, that's what I was looking for! Thanks! – pommy Aug 15 '19 at 15:14
  • 1
    @kontiki The odd behavior of the picker continues. When the app goes to the background and is opened again the picker has extra non-functioning buttons added. Do you have any ideas? – K. Law Sep 11 '19 at 20:52
  • @K.Law Can confirm behavior. Also when toggling Dark Mode. I submitted a bug report (FB7320979). Proposed workaround here doesn't work for this bug. Also the VStack is not necessary. The bug occurs regardless of the hierarchy. – hidden-username Sep 24 '19 at 16:55
  • Please be aware that SwiftUI now uses a `.tabItem` modifier, instead of a `TabItem` view. – Hardy Sep 25 '19 at 07:43