Skip to content

Instantly share code, notes, and snippets.

@Alhomaidhi
Created September 5, 2021 14:11
Show Gist options
  • Save Alhomaidhi/5a77ca436585d8d8b3b671b98547783f to your computer and use it in GitHub Desktop.
Save Alhomaidhi/5a77ca436585d8d8b3b671b98547783f to your computer and use it in GitHub Desktop.
A SwiftUI button that reacts to networking requests.
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
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment