-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic SwiftUI Avatar picker (#318)
* Add CachedAsyncImage and AvatarView * Some improvements * Add demo page * Format * Small update * Remove unused * Fix typo * Replace the init parameters with a decorator function * Format * Remove DefaultAvatarContent * Simplification * One more simplification * Revert "One more simplification" This reverts commit 43880de. * Make swiftformat * Update ProfileService * Add AvatarPicker * Add support for using `package` access on Swift types * Use an enum for the state of the models array * Use Result enum --------- Co-authored-by: Andrew Montgomery <[email protected]>
- Loading branch information
1 parent
d154972
commit 1aefee3
Showing
14 changed files
with
486 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
Demo/Demo/Gravatar-SwiftUI-Demo/DemoAvatarPickerView.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import SwiftUI | ||
@testable import GravatarUI | ||
|
||
@MainActor | ||
struct DemoAvatarPickerView: View { | ||
|
||
@AppStorage("pickerEmail") private var email: String = "" | ||
@AppStorage("pickerToken") private var token: String = "" | ||
@State private var isSecure: Bool = true | ||
@StateObject private var avatarPickerModel = AvatarPickerViewModel(email: .init(""), authToken: "") | ||
|
||
var body: some View { | ||
VStack(alignment: .leading, spacing: 5) { | ||
VStack(alignment: .leading, spacing: 5) { | ||
TextField("Email", text: $email) | ||
.font(.callout) | ||
.textInputAutocapitalization(.never) | ||
.keyboardType(.emailAddress) | ||
.disableAutocorrection(true) | ||
.onChange(of: email) { oldValue, newValue in | ||
avatarPickerModel.update(email: email) | ||
} | ||
HStack { | ||
tokenField() | ||
.font(.callout) | ||
.textInputAutocapitalization(.never) | ||
.disableAutocorrection(true) | ||
.onChange(of: token) { oldValue, newValue in | ||
avatarPickerModel.update(authToken: token) | ||
} | ||
Button(action: { | ||
isSecure.toggle() | ||
}) { | ||
Image(systemName: isSecure ? "eye.slash" : "eye") | ||
.foregroundColor(.gray) | ||
} | ||
} | ||
Divider() | ||
} | ||
.padding(.horizontal) | ||
|
||
AvatarPickerView(model: avatarPickerModel).onAppear() { | ||
avatarPickerModel.update(email: email) | ||
avatarPickerModel.update(authToken: token) | ||
} | ||
.frame(maxWidth: .infinity, maxHeight: .infinity) | ||
} | ||
} | ||
|
||
@ViewBuilder | ||
func tokenField() -> some View { | ||
if isSecure { | ||
SecureField("Token", text: $token) | ||
} else { | ||
TextField("Token", text: $token) | ||
} | ||
} | ||
} | ||
|
||
#Preview { | ||
DemoAvatarPickerView() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
Sources/GravatarUI/SwiftUI/AvatarPicker/AvatarImageModel.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import UIKit | ||
|
||
struct AvatarImageModel: Hashable, Identifiable { | ||
enum Source: Hashable { | ||
case remote(url: String) | ||
case local(image: UIImage) | ||
} | ||
|
||
let id: String | ||
let isLoading: Bool | ||
let source: Source | ||
|
||
var url: URL? { | ||
guard case .remote(let url) = source else { | ||
return nil | ||
} | ||
return URL(string: url) | ||
} | ||
|
||
init(id: String, source: Source, isLoading: Bool = false) { | ||
self.id = id | ||
self.source = source | ||
self.isLoading = isLoading | ||
} | ||
|
||
func togglingLoading() -> AvatarImageModel { | ||
AvatarImageModel(id: id, source: source, isLoading: !isLoading) | ||
} | ||
} |
Oops, something went wrong.