Alert 버튼 1개 / 버튼 2개 표시되게 커스텀한 예제 

 

[CustomDialog.swift]

import SwiftUI

// MARK: View 백그라운드 투명화
struct ClearViewBackground: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        DispatchQueue.main.async {
            view.superview?.superview?.backgroundColor = .clear
        }
        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {}
}

struct CustomDialog: View {
    @Binding var isActive: Bool
    
    private let title: String
    private let message: String
    private var cancelButtonTitle: String?
    private let conformButtonTitle: String
    private var cancelAction: (() -> ())?
    private let conformAction: () -> ()
    
    // 버튼 1개
    init(isActive: Binding<Bool>, 
         title: String,
         message: String,
         conformButtonTitle: String,
         conformAction: @escaping () -> Void) {
        self._isActive = isActive
        self.title = title
        self.message = message
        self.conformButtonTitle = conformButtonTitle
        self.conformAction = conformAction
    }
    
    // 버튼 2개
    init(isActive: Binding<Bool>, 
         title: String,
         message: String,
         cancelButtonTitle: String?,
         conformButtonTitle: String,
         cancelAction: ( () -> Void)?,
         conformAction: @escaping () -> Void) {
        self._isActive = isActive
        self.title = title
        self.message = message
        self.cancelButtonTitle = cancelButtonTitle
        self.conformButtonTitle = conformButtonTitle
        self.cancelAction = cancelAction
        self.conformAction = conformAction
    }
    
    var body: some View {
        ZStack {
            Color(.black)
                .opacity(0.3)
            
            VStack {
                Text(title)
                    .font(.title2)
                    .bold()
                    .padding()
                
                Text(message)
                    .font(.body)
                
                HStack {
                    // 왼쪽 취소 버튼
                    if cancelAction != nil {
                        Button {
                            cancelAction?()
                            close()
                        } label: {
                            ZStack {
                                RoundedRectangle(cornerRadius: 30)
                                    .foregroundColor(.gray)
                                
                                Text(cancelButtonTitle ?? "")
                                    .font(.system(size: 16, weight: .bold))
                                    .foregroundColor(.white)
                                    .padding()
                            }
                        }
                    }
                    
                    // 오른쪽 컨펌 버튼
                    Button {
                        conformAction()
                        close()
                    } label: {
                        ZStack {
                            RoundedRectangle(cornerRadius: 30)
                                .foregroundColor(.red)
                            
                            Text(conformButtonTitle)
                                .font(.system(size: 16, weight: .bold))
                                .foregroundColor(.white)
                                .padding()
                        }
                    }
                }
                .padding(.top, 10)
            }
            .fixedSize(horizontal: false, vertical: true)
            .padding()
            // background + clipShape radius 적용 박스
            .background(Color.white)
            .clipShape(RoundedRectangle(cornerRadius: 20))
            .overlay (      //clipShape가 아닌 맨 아래에 위치시키면 x 버튼이 박스에서 벋어남
                VStack {
                    HStack {
                        Spacer()
                        Button {
                            close()
                        } label: {
                            Image(systemName: "xmark")
                                .font(.title2)
                        }
//                    .tint(Color.black) // IOS 16
                        .foregroundColor(.black)
                    }
                    Spacer()
                }
                    .padding()
            )
            .shadow(radius: 20)
            .padding()
        }
        .background(ClearViewBackground())
        .ignoresSafeArea()
    }
    
    func close() {
        isActive = false
    }
}

#Preview {
    //버튼 2개 사용
    
    CustomDialog(isActive: .constant(true) ,
                 title: "Title",
                 message: "I am building my video editing app and, I need a dialog to present before asking for permission to access photos.",
                 cancelButtonTitle: "취소",
                 conformButtonTitle: "확인",
                 cancelAction: {
        
    }, conformAction: {
        
    })
    
    //버튼 1개 사용
    /*
    CustomDialog(isActive: .constant(true) ,
                 title: "Title",
                 message: "I am building my video editing app and, I need a dialog to present before asking for permission to access photos.",
                 cancelButtonTitle: "취소",
                 conformButtonTitle: "확인",
                 cancelAction: nil, conformAction: {
        
    })*/
}

 

 

[사용방법]

import SwiftUI

struct ContentView: View {
    @State var isShowDialog: Bool = false
    
    var body: some View {
        ZStack {
            VStack {
                Button {
                   //fullScreenCover 애니메이션 비활성화
                   //애니메이션 사용시 주석처리
                    UIView.setAnimationsEnabled(false)
                    isShowDialog.toggle()
                } label: {
                    HStack {
                        Image(systemName: "globe")
                        Text("Custom Dialog")
                    }
                }
                .foregroundColor(Color.white)
                .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) // foreground 와 background 사이의 padding
                .background(
                    LinearGradient(
                        colors: [.red, .blue, .green, .yellow],
                        startPoint: .leading,
                        endPoint: .trailing
                    )
                    .cornerRadius(20)
                )
                //Border 그라데이션 라인
                .overlay(
                    RoundedRectangle(cornerRadius: 20)
                        .stroke(LinearGradient(
                            colors: [.red, .blue, .green, .yellow],
                            startPoint: .trailing,
                            endPoint: .leading
                        ), lineWidth: 1)
                )
                .fullScreenCover(isPresented: $isShowDialog, onDismiss: {
                    print("CustomDialog Dismiss!!!")
                    
                    //View 애니메이션 활성화 그래야 다른 동작시 애니메이션 동작함
                    //애니메이션 사용시 주석처리
                    UIView.setAnimationsEnabled(true)
                } ,content: {
                    //버튼 2개 사용
                    
                    CustomDialog(isActive: $isShowDialog,
                                 title: "Title",
                                 message: "I am building my video editing app and, I need a dialog to present before asking for permission to access photos.",
                                 cancelButtonTitle: "취소",
                                 conformButtonTitle: "확인",
                                 cancelAction: {
                        print("CustomDialog cancel action!!")
                    }, conformAction: {
                        print("CustomDialog conform action!!")
                    })
                    
                    //버튼 1개 사용
                    /*
                    CustomDialog(isActive: $isShowDialog ,
                                 title: "Title",
                                 message: "I am building my video editing app and, I need a dialog to present before asking for permission to access photos.",
                                 conformButtonTitle: "확인",
                                 conformAction: {
                        print("CustomDialog conform action!!")
                    })*/
                })
            }
        }
    }
}

#Preview {
    ContentView()
}

+ Recent posts