| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- /*
- * Copyright 2022 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #if !arch(arm) && !arch(i386)
- import SwiftUI
- import CoreGraphics
- // MARK: - Sign-In Button
- /// A Google Sign In button to be used in SwiftUI.
- @available(iOS 13.0, macOS 10.15, *)
- public struct GoogleSignInButton: View {
- /// An object containing the styling information needed to create the button.
- @ObservedObject public var viewModel: GoogleSignInButtonViewModel
- private let action: () -> Void
- private let fontLoaded: Bool
- /// Creates an instance of the Google Sign-In button for use in SwiftUI
- /// - parameter viewModel: An instance of `GoogleSignInButtonViewModel`
- /// containing information on the button's scheme, style, and state.
- /// Defaults to `GoogleSignInButtonViewModel` with its standard defaults.
- /// - parameter action: A closure to use as the button's action upon press.
- /// - seealso: Refer to `GoogleSignInButtonViewModel.swift` for its defaults.
- public init(
- viewModel: GoogleSignInButtonViewModel = GoogleSignInButtonViewModel(),
- action: @escaping () -> Void
- ) {
- self.viewModel = viewModel
- self.action = action
- self.fontLoaded = Font.loadCGFont()
- }
- /// A convenience initializer to create a Google Sign-In button in SwiftUI
- /// with scheme, style, and state.
- /// - parameter scheme: The `GoogleSignInButtonColorScheme` to use. Defaults
- /// to `.light`.
- /// - parameter style: The `GoogleSignInButtonStyle` to use. Defaults to
- /// `.standard`.
- /// - parameter state: The `GoogleSignInButtonState` to use. Defaults to
- /// `.normal`.
- /// - parameter action: A closure to use as the button's action upon press.
- public init(
- scheme: GoogleSignInButtonColorScheme = .light,
- style: GoogleSignInButtonStyle = .standard,
- state: GoogleSignInButtonState = .normal,
- action: @escaping () -> Void
- ) {
- let vm = GoogleSignInButtonViewModel(
- scheme: scheme,
- style: style,
- state: state
- )
- self.init(viewModel: vm, action: action)
- }
- public var body: some View {
- Button(action: action) {
- switch viewModel.style {
- case .icon:
- ZStack {
- RoundedRectangle(cornerRadius: googleCornerRadius)
- .fill(viewModel.buttonStyle.colors.iconColor)
- .border(viewModel.buttonStyle.colors.iconBorderColor)
- Image.signInButtonImage
- }
- case .standard, .wide:
- HStack(alignment: .center) {
- ZStack {
- RoundedRectangle(cornerRadius: googleCornerRadius)
- .fill(
- viewModel.state == .disabled ?
- .clear : viewModel.buttonStyle.colors.iconColor
- )
- .frame(
- width: iconWidth - iconPadding,
- height: iconWidth - iconPadding
- )
- Image.signInButtonImage
- }
- .padding(.leading, 1)
- Text(viewModel.style.buttonText)
- .fixedSize()
- .padding(.trailing, textPadding)
- .frame(
- width: viewModel.style.widthForButtonText,
- height: buttonHeight,
- alignment: .leading
- )
- Spacer()
- }
- }
- }
- .font(signInButtonFont)
- .buttonStyle(viewModel.buttonStyle)
- .disabled(viewModel.state == .disabled)
- .simultaneousGesture(
- DragGesture(minimumDistance: 0)
- .onChanged { _ in
- guard viewModel.state != .disabled,
- viewModel.state != .pressed else { return }
- viewModel.state = .pressed
- }
- .onEnded { _ in
- guard viewModel.state != .disabled else { return }
- viewModel.state = .normal
- }
- )
- }
- }
- // MARK: - Google Icon Image
- @available(iOS 13.0, macOS 10.15, *)
- private extension Image {
- static var signInButtonImage: Image {
- guard let iconURL = Bundle.urlForGoogleResource(
- name: googleImageName,
- withExtension: "png"
- ) else {
- fatalError("Unable to load Google icon image url: \(Image.Error.unableToLoadGoogleIcon(name: googleImageName))")
- }
- #if os(iOS) || targetEnvironment(macCatalyst)
- guard let uiImage = UIImage(contentsOfFile: iconURL.path) else {
- fatalError("Unable to load Google icon image url: \(Image.Error.unableToLoadGoogleIcon(name: googleImageName))")
- }
- return Image(uiImage: uiImage)
- #elseif os(macOS)
- guard let nsImage = NSImage(contentsOfFile: iconURL.path) else {
- fatalError("Unable to load Google icon image url: \(Image.Error.unableToLoadGoogleIcon(name: googleImageName))")
- }
- return Image(nsImage: nsImage)
- #else
- fatalError("Unrecognized platform for SwiftUI sign in button image")
- #endif
- }
- enum Error: Swift.Error {
- case unableToLoadGoogleIcon(name: String)
- }
- }
- // MARK: - Google Font
- @available(iOS 13.0, macOS 10.15, *)
- private extension GoogleSignInButton {
- var signInButtonFont: Font {
- guard fontLoaded else {
- return .system(size: fontSize, weight: .bold)
- }
- return .custom(fontNameRobotoBold, size: fontSize)
- }
- }
- @available(iOS 13.0, macOS 10.15, *)
- private extension Font {
- /// Load the font for the button.
- /// - returns A `Bool` indicating whether or not the font was loaded.
- static func loadCGFont() -> Bool {
- // Check to see if the font has already been loaded
- #if os(iOS) || targetEnvironment(macCatalyst)
- if let _ = UIFont(name: fontNameRobotoBold, size: fontSize) {
- return true
- }
- #elseif os(macOS)
- if let _ = NSFont(name: fontNameRobotoBold, size: fontSize) {
- return true
- }
- #else
- fatalError("Unrecognized platform for SwiftUI sign in button font")
- #endif
- guard let fontURL = Bundle.urlForGoogleResource(
- name: fontNameRobotoBold,
- withExtension: "ttf"
- ), let dataProvider = CGDataProvider(filename: fontURL.path),
- let newFont = CGFont(dataProvider) else {
- return false
- }
- return CTFontManagerRegisterGraphicsFont(newFont, nil)
- }
- }
- #endif // !arch(arm) && !arch(i386)
|