|
struct NetworkingRectangularButton: View { |
|
|
|
@State var buttonText: String |
|
@State var backgroundColor: Color |
|
@Binding var buttonState: ButtonState |
|
|
|
let cornerRadius: CGFloat = 6 |
|
let circleCornerRaduis: CGFloat = UIScreen.main.bounds.width / 2 |
|
let width: CGFloat = UIScreen.main.bounds.width / 1.2 |
|
let height: CGFloat = 48 |
|
let generator = UINotificationFeedbackGenerator() |
|
let action: ()->() |
|
|
|
init(action: @escaping ()->(), buttonText: String, backgroundColor: Color, buttonState: Binding<ButtonState>) { |
|
self.action = action |
|
self.buttonText = buttonText |
|
self.backgroundColor = backgroundColor |
|
self._buttonState = buttonState |
|
} |
|
|
|
var isLoading: Bool { buttonState == .loading } |
|
var isSuccess: Bool { buttonState == .success } |
|
var isError: Bool { buttonState == .error } |
|
var isNotNormal: Bool { buttonState != .normal } |
|
|
|
enum ButtonState { |
|
case normal |
|
case loading |
|
case success |
|
case error |
|
} |
|
|
|
var body: some View { |
|
|
|
Button(action: action, label: { |
|
Text(isNotNormal ? "" : buttonText) |
|
.frame(width: isNotNormal ? height : width, height: height) |
|
.background(backgroundColor) |
|
.foregroundColor(.white) |
|
.cornerRadius(isNotNormal ? circleCornerRaduis : cornerRadius) |
|
.overlay(successOverlay()) |
|
.overlay(errorOverlay()) |
|
.overlay(loadingOverlay()) |
|
.animation(.easeIn) |
|
}) |
|
.disabled(isNotNormal) |
|
.onChange(of: buttonState, perform: { value in |
|
switch buttonState { |
|
case .normal: |
|
break |
|
case .loading: |
|
break |
|
case .success: |
|
backToNormal() |
|
case .error: |
|
backToNormal() |
|
} |
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
@ViewBuilder |
|
func successOverlay() -> some View { |
|
Image(systemName: "checkmark") |
|
.foregroundColor(Color(UIColor.black)) |
|
.scaleEffect(isSuccess ? 1.2 : 0) |
|
.animation(.easeIn(duration: 0.45)) |
|
} |
|
|
|
@ViewBuilder |
|
func errorOverlay() -> some View { |
|
Image(systemName: "xmark") |
|
.foregroundColor(Color(UIColor.black)) |
|
.scaleEffect(isError ? 1.2 : 0) |
|
.animation(.easeIn(duration: 0.45)) |
|
} |
|
|
|
@ViewBuilder |
|
func loadingOverlay() -> some View { |
|
isLoading ? ProgressView() : nil |
|
} |
|
|
|
func backToNormal() { |
|
DispatchQueue.global(qos: .background).async { |
|
Thread.sleep(forTimeInterval: 0.45) |
|
DispatchQueue.main.async { |
|
if buttonState == .success { |
|
generator.notificationOccurred(.success) |
|
} else if buttonState == .error { |
|
generator.notificationOccurred(.error) |
|
} |
|
} |
|
Thread.sleep(forTimeInterval: 1) |
|
DispatchQueue.main.async { |
|
if buttonState != .loading { |
|
buttonState = .normal |
|
} |
|
} |
|
} |
|
} |
|
} |