Eric Agredo
Eric Agredo

Reputation: 539

Change Sign In With Apple button style based on current color scheme

I'm having some issues with SwiftUI's SignInWithAppleButton's signInWithAppleButtonStyle. I am trying to change the color of the button based on the user's current scheme or if it changes. This is iOS 14 and SwiftUI:

@Environment(\.colorScheme) var currentScheme
@State var appleButtonWhite = false

VStack{
SignInWithAppleButton(
                .signUp,
                onRequest: { request in              
                    request.requestedScopes = [.fullName, .email]
                },
                onCompletion: { result in
                    switch result {
                    case .success (let authResults):
                        print("Authorization successful.")
       
                    case .failure (let error):
                        print("Authorization failed: " + error.localizedDescription)
                    }
                }
            )
            .frame(minWidth: 0, maxWidth: .infinity)
            .signInWithAppleButtonStyle(appleButtonWhite ? .white : .black)
}
.onChange(of: currentScheme, perform: { (scheme) in
        if scheme == .light
        {
            appleButtonWhite = false
        }
        else
        {
            appleButtonWhite = true
        }
    })

When appleButtonWhite changes values, it renders the view as it should since the state is changing. When I debug the button it has the correct appleButtonWhite value, but for some reason the style just never changes. I have no idea why. I have done many style changes in my code with regular buttons and it works correctly based on different states. Any ideas why Apple's doesn't change?

Upvotes: 6

Views: 3276

Answers (4)

Atif
Atif

Reputation: 286

For button to take new style, you need to set different id to it as signInWithAppleButtonStyle don't seem to update internal state of this view, which prevent re-rending of this view

SignInWithAppleButton(
       onRequest: { request in
                    request.requestedScopes = [.email,.fullName]
                },
       onCompletion: resultHandler(result:)
    )
        .signInWithAppleButtonStyle(colorScheme == .dark ? .white : .black)
        .id(colorScheme == .dark ? "dark-btn": "light-btn")

Upvotes: 0

ali.ios
ali.ios

Reputation: 51

You can assign an ID to the 'Sign in with Apple' button so that when you change the color scheme, you can refresh this view by its ID. Please see the example below. This logic is valid for any other views that don't refresh their UI.

@State private var signInAppleButtonId = UUID().uuidString
@Environment(\.colorScheme) private var scheme

@ViewBuilder
private var SignInWithApple: some View {
    SignInWithAppleButton(
        onRequest: { request in
            ///
        },
        onCompletion: { result in
            ///
        }
    )
    .id(signInAppleButtonId)
    .signInWithAppleButtonStyle(scheme == .light ? .black : .white)
    .onChange(of: scheme, { oldValue, newValue in
        signInAppleButtonId = UUID().uuidString
    })
    .frame(width: 280, height: 45, alignment: .center)
    .shadow(radius: 10)
}

Upvotes: 0

ViOS
ViOS

Reputation: 233

We can write even more elegant solution. Based on the mrjohnsly answer above 👆

Here's a resource that explain how to efficiently use Swift views https://developer.apple.com/wwdc21/10022

struct ContentView: View {
    @Environment(\.colorScheme) var currentScheme
    
    var body: some View {
        VStack {
            SignInWithAppleButton { request in
                // request handeling
            } onCompletion: { result in
                // result handeling
            }
            .signInWithAppleButtonStyle(currentScheme == .light ? .black : .white) // <- here
            .frame(width: 250, height: 45, alignment: .center)
        }
    }
}

Upvotes: 3

mrjohnsly
mrjohnsly

Reputation: 320

I managed to solve this by moving the SignInWithAppleButton to its own View, without signInWithAppleButtonStyle

Then using @Environment(\.colorScheme) create an if statement and import the SignInWithAppleButtonView styling with signInWithAppleButtonStyle.

import SwiftUI
import AuthenticationServices

struct ContentView: View {
    
    @Environment(\.colorScheme) var currentScheme
    
    var body: some View {
        if self.currentScheme == .light {
            SignInWithAppleButtonView()
                .signInWithAppleButtonStyle(.black)
        } else {
            SignInWithAppleButtonView()
                .signInWithAppleButtonStyle(.white)
        }
    }
}

struct SignInWithAppleButtonView: View {
    var body: some View {
        SignInWithAppleButton(
            .signUp,
            onRequest: {_ in },
            onCompletion: {_ in }
        )
    }
}

Upvotes: 10

Related Questions