Skip to content

Instantly share code, notes, and snippets.

@vurgunmert
Created August 24, 2024 10:13
Show Gist options
  • Save vurgunmert/e888d708f1a404a8b3600b2d50c6ec79 to your computer and use it in GitHub Desktop.
Save vurgunmert/e888d708f1a404a8b3600b2d50c6ec79 to your computer and use it in GitHub Desktop.
SwiftUI Native and Custom Alerts Storybooks tvOS
//
// SwiftUIAlertsStorybooksView.swift
// TemplateAppTvOS
//
// Created by Mert Vurgun on 24.08.2024.
//
import SwiftUI
// MARK: - Main View: SwiftUIAlertsStorybooksView
struct SwiftUIAlertsStorybooksView: View {
@State private var showAlert = false
@State private var showActionSheet = false
@State private var showCustomAlert = false
@State private var currentAlert: Alert = Alert(title: Text("Default"))
@FocusState private var focusedField: FocusField?
// Enum to define non-focusable state when alert is active
enum FocusField: Hashable {
case none
}
var body: some View {
ZStack {
// Main content with buttons
VStack(spacing: 20) {
Button(action: showBasicAlert) {
Text("Basic Alert")
.font(.title2)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
Button(action: showDestructiveAlert) {
Text("Destructive Alert")
.font(.title2)
.padding()
.background(Color.red)
.foregroundColor(.white)
.cornerRadius(10)
}
Button(action: toggleActionSheet) {
Text("Action Sheet")
.font(.title2)
.padding()
.background(Color.orange)
.foregroundColor(.white)
.cornerRadius(10)
}
Button(action: showTextInputAlert) {
Text("Text Input Alert")
.font(.title2)
.padding()
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(10)
}
Button(action: { showCustomAlert.toggle() }) {
Text("Custom Alert")
.font(.title2)
.padding()
.background(Color.purple)
.foregroundColor(.white)
.cornerRadius(10)
}
}
.padding()
.alert(isPresented: $showAlert) {
currentAlert
}
.actionSheet(isPresented: $showActionSheet) {
ActionSheet(title: Text("Choose an Option"),
message: Text("Select one of the options below."),
buttons: [
.default(Text("Option 1"), action: { print("Option 1 selected") }),
.default(Text("Option 2"), action: { print("Option 2 selected") }),
.default(Text("Option 3"), action: { print("Option 3 selected") }),
.cancel()
])
}
.disabled(showCustomAlert) // Disable interaction with background content when custom alert is shown
// Overlay and Custom Alert Display
if showCustomAlert {
Color.black.opacity(0.5) // Semi-transparent overlay to focus attention on the alert
.ignoresSafeArea()
.onTapGesture {
// Prevent accidental dismissal by ignoring tap events
}
CustomAlertView(showAlert: $showCustomAlert, title: "Attention", message: "Are you sure you want to proceed?", cancelButtonTitle: "No", okButtonTitle: "Yes")
.transition(.move(edge: .bottom).combined(with: .opacity))
.zIndex(1) // Ensure the alert appears above other content
}
}
.animation(.easeInOut, value: showCustomAlert)
.focused($focusedField, equals: showCustomAlert ? Optional.none : nil) // Prevent focus on main view when alert is shown
}
// MARK: - Alert Handling Methods
// Shows a basic alert
private func showBasicAlert() {
currentAlert = Alert(title: Text("Basic Alert"),
message: Text("This is a basic alert."),
dismissButton: .default(Text("OK")))
showAlert = true
}
// Shows a destructive alert with delete and cancel options
private func showDestructiveAlert() {
currentAlert = Alert(title: Text("Delete Item"),
message: Text("Are you sure you want to delete this item?"),
primaryButton: .destructive(Text("Delete")),
secondaryButton: .cancel())
showAlert = true
}
// Toggles the action sheet display
private func toggleActionSheet() {
showActionSheet = true
}
// Shows an alert indicating that text input is not supported in SwiftUI alerts
private func showTextInputAlert() {
currentAlert = Alert(title: Text("Text Input Alert"),
message: Text("Text input is not directly supported in SwiftUI alerts."),
dismissButton: .default(Text("OK")))
showAlert = true
}
}
// MARK: - Preview
struct SwiftUIAlertsStorybooksView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIAlertsStorybooksView()
}
}
struct CustomAlertView: View {
@Binding var showAlert: Bool
@FocusState private var focusedField: Field?
// Enum to define focusable fields (Cancel and OK buttons)
enum Field: Hashable {
case cancel, ok
}
var title: String = "Custom Alert"
var message: String = "This is a fully customized alert created with SwiftUI. You can add any content here."
var cancelButtonTitle: String = "Cancel"
var okButtonTitle: String = "OK"
var body: some View {
VStack(spacing: 20) {
// Title Section
Text(title)
.font(.system(size: 22, weight: .bold, design: .rounded))
.foregroundColor(Color.blue)
.multilineTextAlignment(.center)
.padding(.horizontal)
// Message Section
Text(message)
.font(.system(size: 17, weight: .regular, design: .rounded))
.foregroundColor(Color.gray)
.multilineTextAlignment(.center)
.padding(.horizontal)
// Buttons Section with Focus Management
HStack(spacing: 15) {
Button(action: {
showAlert = false
}) {
Text(cancelButtonTitle)
.font(.system(size: 18, weight: .semibold, design: .rounded))
.frame(maxWidth: .infinity)
.padding(.vertical, 12)
.background(Color(UIColor.black))
.foregroundColor(.primary)
.cornerRadius(12)
}
.focused($focusedField, equals: .cancel) // Manage focus for tvOS
Button(action: {
showAlert = false
}) {
Text(okButtonTitle)
.font(.system(size: 18, weight: .semibold, design: .rounded))
.frame(maxWidth: .infinity)
.padding(.vertical, 12)
.background(LinearGradient(gradient: Gradient(colors: [Color.purple, Color.blue]), startPoint: .leading, endPoint: .trailing))
.foregroundColor(.white)
.cornerRadius(12)
}
.focused($focusedField, equals: .ok) // Manage focus for tvOS
}
.padding(.horizontal)
}
.padding(.vertical, 20)
.background(
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(Color(UIColor.red))
.shadow(color: Color.black.opacity(0.2), radius: 10, x: 0, y: 5)
)
.frame(maxWidth: 300)
.padding(.horizontal, 40)
.onAppear {
focusedField = .ok // Initial focus on OK button
}
.onChange(of: showAlert) { _ in
if showAlert {
focusedField = .ok // Retain focus within the alert when shown
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment