Home > OS >  SwiftUI menu button is slow to resize when title string changes length
SwiftUI menu button is slow to resize when title string changes length

Time:12-22

I have the following SwiftUI menu:

import SwiftUI

struct ContentView: View {

    private enum Constants {
        static let arrowIcon = Image(systemName: "chevron.down")
        static let background = Color.blue
        static let buttonFont = Font.system(size: 14.0)
    }


    @State private var menuIndex = 0
    private let menuItems = ["This year", "Last Year"]

    private var menuTitle: String {
        guard menuItems.indices.contains(menuIndex) else { return "" }

        return menuItems[menuIndex]
    }

    // MARK: - Views

    var body: some View {
        makeMenu()
    }

    // MARK: - Buttons

    private func menuItemTapped(title: String) {
        guard let index = menuItems.firstIndex(of: title) else { return }

        menuIndex = index
    }

    // MARK: - Factory

    @ViewBuilder private func makeMenu() -> some View {
        Menu {
            ForEach(menuItems, id: \.self) { title in
                Button(title, action: { menuItemTapped(title: title) })
            }
        } label: {
            Text("\(menuTitle) \(Constants.arrowIcon)")
                .font(Constants.buttonFont)
                .fontWeight(.bold)
        }
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

When a menu item is tapped the index to the title array is updated causing the Text title to update. This works as desired however the text is slow to resize to the changes Last Year... will briefly show before it resizes correctly.

enter image description here

What am I doing wrong here?

CodePudding user response:

This is because the size of the label is being set but not being changed. If you're okay with it, setting a maxWidth of .infinity on the frame of the Label gives you this capability.

@ViewBuilder private func makeMenu() -> some View {
        VStack {
            Menu {
                ForEach(menuItems, id: \.self) { title in
                    Button(title, action: { menuItemTapped(title: title) })
                }
            } label: {
                Text("\(menuTitle) \(Constants.arrowIcon)")
                    .font(Constants.buttonFont)
                    .fontWeight(.bold)
                    .frame(maxWidth: .infinity) //--> Add this line
        }
        }
    }
  • Related