2
\$\begingroup\$

I decided to write a simple tab bar for macOS using Swift.

tabs.swift

import SwiftUI
import Foundation
import Combine

class ViewRouter: ObservableObject {
  @Published var currentView = "home"
}

struct Tabs: View {
  
  @ObservedObject var viewRouter = ViewRouter()
  
  var body: some View {
    GeometryReader { geometry in
      VStack {
        if self.viewRouter.currentView == "home" {
          Text("Home")
        } else if self.viewRouter.currentView == "settings" {
          Text("Settings")
        }
        Spacer()
        
        HStack {
          { () -> Text in
            if self.viewRouter.currentView == "home" {
              return Text("Home").foregroundColor(Color.blue)
            } else {
              return Text("Home")
            }
          }()
          .onTapGesture {
            self.viewRouter.currentView = "home"
          }
          
          { () -> Text in
            if self.viewRouter.currentView == "settings" {
              return Text("Settings").foregroundColor(Color.blue)
            } else {
              return Text("Settings")
            }
          }()
          .onTapGesture {
            self.viewRouter.currentView = "settings"
          }
        }
      }.edgesIgnoringSafeArea(.bottom).padding(20)
    }
  }
}

In ContentView.swift, I just display this using Tabs() in the ContentView struct.

In the HStack, I use two anonymous functions to give the text in the tab bar a blue color when that item is selected:

{ () -> Text in
  if self.viewRouter.currentView == "home" {
    return Text("Home").foregroundColor(Color.blue)
  } else {
    return Text("Home")
  }
}()
  .onTapGesture {
    self.viewRouter.currentView = "home"
  }

I also do this for the second item in the tab bar.

Is this the best (fastest and most concise) way to do this, or is there a better way?

\$\endgroup\$
2
\$\begingroup\$

There is a shorter and faster way, using ternary operators.

You could use

Text("Home").foregroundColor(self.viewRouter.currentView == "home" ? Color.blue: Color.black)

instead of

{ () -> Text in
  if self.viewRouter.currentView == "home" {
    return Text("Home").foregroundColor(Color.blue)
  } else {
    return Text("Home")
  }
}()

However, with the advent of macOS Mojave 10.14 two years ago, users had an option to enable dark mode. So, unless you've explicitly disabled dark mode in your app, Color.black will blend in with the rest of your app bar. To prevent this, check for dark mode with UserDefaults.standard.object(forKey: "AppleInterfaceStyle"). If the user is using light mode, UserDefaults.standard.object(forKey: "AppleInterfaceStyle") will be nil, and if it's dark mode, it will be "Dark"

Text("Home").foregroundColor(self.viewRouter.currentView == "home" ? Color.blue: UserDefaults.standard.object(forKey: "AppleInterfaceStyle") == nil ? Color.black: Color.white)

As @BenLeggiero has suggested, this is a much shorter way to do the above:

Text("Home").foregroundColor(viewRouter.currentView == "home" ? .blue : .primary)
\$\endgroup\$
3
  • \$\begingroup\$ You can check for dark mode with @Environment(\.colorScheme) var colorScheme \$\endgroup\$ – Patrick Wynne Dec 5 '20 at 18:57
  • \$\begingroup\$ Is there a reason to not use Text("Home").foregroundColor(viewRouter.currentView == "home" ? .blue : .primary)? \$\endgroup\$ – Ben Leggiero Dec 7 '20 at 15:52
  • 1
    \$\begingroup\$ @BenLeggiero No, there's not. In fact, I'll update my answer right now to reflect that. \$\endgroup\$ – GalaxyCat105 Dec 7 '20 at 16:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.