Returning a button from a function in SwiftUI

Viewed 62

I need to create a different button based on configuration and then apply a common theme to any button that was created. Is this even possible in SwiftUI?

func makeButton(type: ButtonType) -> some View {
        var button: Button
        
        switch type {
        case .overview:
            button = overviewButton
        case .location:
            button = locationButton
        }
        button
            .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10))
            .background(Rectangle()
                .foregroundColor(theme.primaryColor)
                .cornerRadius(10)
                .shadow(color: .gray, radius: 3.0, x: -1.0, y: 1.0)
            )
            .foregroundColor(theme.textColor)
        return button
    }
    
    @ViewBuilder var overviewButton: some View {
        Button(action: { viewModel.showOverview(isUserInitiated: true) },
               label: {
            Image("overviewIcon", bundle: default.bundle)
                .renderingMode(.template)
                .foregroundColor(theme.textColor)
        })
        .opacity(viewModel.isOverviewMode ? 0.0 : 1.0)
    }

    @ViewBuilder var locationButton: some View {
        Button(action: { viewModel.showLocation(isUserInitiated: true) },
               label: {
            Image("locationIcon", bundle: default.bundle)
                .renderingMode(.template)
                .foregroundColor(theme.textColor)
        })
        .opacity(viewModel.isLocationMode ? 0.0 : 1.0)
    }

But I get an error in the line I declare button:

Reference to generic type 'Button' requires arguments in <...>

How to we declare a variable of type Button?

1 Answers

That might not be the best solution (mainly, because of code duplication, of course,) but this should work:

@ViewBuilder func makeButton(type: ButtonType) -> some View {
  switch type {
    case .overview:
      overviewButton
        .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10))
        // Etc.
    case .location:
      locationButton
        .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10))
        // Etc.
    }
}

@ViewBuilder var overviewButton: some View {
  // ...
}

@ViewBuilder var locationButton: some View {
  // ...
}

To reduce code duplication, you could extract another method:

func makeButton(type: ButtonType) -> some View {
  makeButtonBase(type: type)
    .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10))
    // Etc.
}

@ViewBuilder func makeButtonBase(type: ButtonType) -> some View {
  switch type {
    case .overview:
      overviewButton
    case .location:
      locationButton
  }
}

@ViewBuilder var overviewButton: some View {
  // ...
}

@ViewBuilder var locationButton: some View {
  // ...
}
Related