seong_hye, the developer

SwiftUI) Toast 생성해보기(normal, enum에 따른 toast) 본문

IOS/SwiftUI

SwiftUI) Toast 생성해보기(normal, enum에 따른 toast)

seong_hye 2022. 10. 29.

 

📘 Toast

사용자가 앱을 조작할 때 화면 하단에 잠깐 표시되는 알림 메시지


🔹 기본 Toast 생성 방법

➡️ toast  struct 

toast 메시지를 보여줄 기본 틀

struct Toast: Equatable {
	var message: String 		//보여줄 메시지
    var duration: Double = 1.5	// 보여질 시간
    var width: Double = 300		// toast 가로 크기
}

 

➡️ toastView

toast를 화면에 어떻게 보여줄 지 보여주는 뷰

struct ToastView: View {
	var message: String
    var width = CGFlat(200)
    var onCancelTapped: (() -> Void)
    
    var body: some View {
    	VStack {
        	Spacer()
            Text(message)
            	.font(.system(size:13))
                .foregroundColor(.white)
                .multilineTextAlignment(.center)
                .padding()
                .frame(minWidth: 0, maxWidth: width)
                .background(.black)
                .opacity(0.7)
                .cornerRadius(30)
                .padding(.horizontal, 18)
            Spacer()
        }
    }
}

 

➡️toast ViewModifier

toast가 어떻게 보여지고 사라질 지를 설정

struct ToastModifier: ViewModifier {
    
    @Binding var toast: Toast?
    @State private var workItem: DispatchWorkItem?
      
    func body(content: Content) -> some View {
      content
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .overlay(
          ZStack {
            mainToastView()
          }.animation(.spring(), value: toast)
        )
        .onChange(of: toast, { oldValue, newValue in
            showToast()
        })
    }
    
    @ViewBuilder func mainToastView() -> some View {
        if let toast = toast {
          VStack {
            ToastView(
              message: toast.message,
              width: toast.width
            ) {
              dismissToast()
            }
            Spacer()
          }
        }
      }
    
    private func showToast() {
      guard let toast = toast else { return }
      
      UIImpactFeedbackGenerator(style: .light)
        .impactOccurred()
      
      if toast.duration > 0 {
        workItem?.cancel()
        
        let task = DispatchWorkItem {
          dismissToast()
        }
        
        workItem = task
        DispatchQueue.main.asyncAfter(deadline: .now() + toast.duration, execute: task)
      }
    }
    
    private func dismissToast() {
        withAnimation {
            toast = nil
        }
        
        workItem?.cancel()
        workItem = nil
    }
}

 

➡️ View Extension

view에서 해당 함수를 쉽게 사용할 수 있도록 추가

extension View {
	func toastView(toast: Binding<Toast?>) -> some View {
    	self.modifier(ToastModifier(toast: toast))
    }
}

 

➡️ 사용 방법

struct ContentView: View {
	@State private var toast: Toast? = nil
    
    var body: some View {
    	VStack {
        	Button {
            	toast = Toast(message: "toast 생성됨")
            } label: {
            	Text("toast 생성")
            }
        }
        .toastView(toast: $toast)
    }
}

 

결과 화면


🔹 상황에 따른 Toast 생성 방법

➡️ ToastStyle

enum ToastStyle {
	case error
    case warning
    case success
    case info
}

extension ToastStyle {
	var themeColor: Color {
    	switch self {
        case .error: return Color.red
        case .warning: return Color.orange
        case .success: return Color.green
        case .info: return Color.blue
        }
    }
    
    var iconFileName: String {
    	switch self {
        case .error: return "xmark.circle.fill"
        case .warning: return "exclamationmark.triangle.fill"
        case .success: return "checkmark.circle.fill"
        case .info: return "info.circle.fill"
    	}
    }
}

 

➡️ toast  struct 

기본 toast 타입에 새로 생성한 style을 추가해서 보여주도록 함

struct Toast: Equatable {
	var type: ToastStyle		// 선택할 타입
	var message: String 		//보여줄 메시지
    var duration: Double = 1.5	// 보여질 시간
    var width: Double = 300		// toast 가로 크기
}

 

➡️ toast View

struct ToastView: View {
	var type: ToastStyle
	var message: String
    var width = CGFlat(200)
    var onCancelTapped: (() -> Void)
    
    var body: some View {
    	VStack {
        	Spacer()
            HStack {
            	Image(systemName: type.iconFileName)
                	.foregroundColor(type.themeColor)
                    
            	Text(message)
            		.font(.system(size:13))
                	.multilineTextAlignment(.center)
                	.padding()
            }
            .frame(minWidth: 0, maxWidth: width)
            .background(.white)
			.overlay (
            	RoundedRectangle(cornerRadius: 10)
                	.stroke(type.themeColor, lineWidth: 2)
                    .shadow(color: Color.black.opacity(0.25), radius: 10, x: 0, y: 1)
            )
            .padding(.horizontal, 18)
            
            Spacer()
        }
    }
}

 

 결과 화면


 

Comments