Notice
Recent Posts
Recent Comments
Link
๊ด€๋ฆฌ ๋ฉ”๋‰ด

lgvv98

[SwiftUI] Toast, popup ๋ณธ๋ฌธ

apple/๐Ÿš SwiftUI & Combine

[SwiftUI] Toast, popup

๐Ÿฅ• ์บ๋Ÿฟ๋งจ 2022. 5. 23. 15:36

Toast, popup

 

โœ… ์ด๋ฒˆ์—๋Š” ์˜คํ”ˆ์†Œ์Šค๋ฅผ ํ™œ์šฉํ•ด์„œ toast์™€ popup์„ ํ•˜๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ•ด๋ณด์ž.

์ด์ „์— ํ”Œ๋Ÿฌํ„ฐ๋‚˜ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœํ•  ๋•Œ, ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋˜ UI์ธ๋ฐ, iOS์—๋„ ๋น„์Šทํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ด์„œ ๊ณต๋ถ€ํ•ด ๋ณด์•˜๋‹ค.

 

โœ… ์˜คํ”ˆ์†Œ์Šค

https://github.com/exyte/PopupView

 

GitHub - exyte/PopupView: Toasts and popups library written with SwiftUI

Toasts and popups library written with SwiftUI. Contribute to exyte/PopupView development by creating an account on GitHub.

github.com

 

์˜คํ”ˆ์†Œ์Šค์—์„œ SPM์„ ์ง€์›ํ•œ๋‹ค๊ณ ํ•œ๋‹ค.

๊ฐœ์ธ์ ์œผ๋กœ CocoaPod๋ณด๋‹ค SPM์„ ์„ ํ˜ธํ•˜๋Š”๋ฐ, ๊ทธ ์ด์œ ๋Š” pod์„ ์ด์šฉํ•˜๋ฉด ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๊ฐ€ pod install์ด๋‚˜ update๋“ฑ์„ ๋‹ค์‹œ ํ•ด์•ผํ•˜์ง€๋งŒ, SPM์œผ๋กœ ํ•˜๋ฉด ๊ทธ๋Ÿด ํ•„์š”๊ฐ€ ์—†์–ด์„œ ํ›จ์”ฌ ํŽธ๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—!

 

 

 

โœ… ์ด๋ฒˆ์—๋Š” ์•Œ์งœ ์ •๋ณด๋“ค์ด ๋งŽ๋‹ค.

 

1. ์ปฌ๋Ÿฌ๋ฅผ Extension ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•

  -> ์—ฌ๊ธฐ ์ฝ”๋“œ๋Š” ๋ด๋‘๋ฉด ์ •๋ง ์ข‹์€ ์ •๋ณด

  -> HexCode๋ฅผ ๊ธฐ์ค€์œผ๋กœ Color๋ฅผ ๋ณ€ํ™˜ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

 

2. ์˜คํ”ˆ์†Œ์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ์˜ˆ์œ ๋””์ž์ธ๊ณผ ์‚ฌ์šฉ์„ฑ์„ ๋†’์—ฌ๋ณด์ž!

  -> ๋ณ€์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๋กœ๋„ some View๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:)

 

 

โœ… Color

Color๋ฅผ extension์„ ํ†ตํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.

import Foundation
import SwiftUI

extension Color {
    init(hexcode: String) {
        let scanner = Scanner(string: hexcode)
        var rgbValue: UInt64 = 0
        
        scanner.scanHexInt64(&rgbValue)
        
        let red = (rgbValue & 0xff0000) >> 16
        let green = (rgbValue & 0xff00) >> 8
        let blue = rgbValue & 0xff
        
        self.init(red: Double(red) / 0xff, green: Double(green) / 0xff, blue: Double(blue) / 0xff)
        
    }
}

โœ… ์ปฌ๋Ÿฌ์ฝ”๋“œ ์‚ฌ์šฉ๋ฒ•

.background(Color(hexcode: "8227b0"))

 

 

โœ… ์˜คํ”ˆ์†Œ์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ์˜ˆ์œ ๋””์ž์ธ๊ณผ ์‚ฌ์šฉ์„ฑ์„ ๋†’์—ฌ๋ณด์ž!

import SwiftUI
import ExytePopupView

struct ContentView: View {
    
    @State var shouldShowBottomSolidMessage : Bool = false
    
    @State var shouldShowBottomToastMessage : Bool = false
    
    @State var shouldShowTopSolidMessage : Bool = false
    
    @State var shouldShowTopToastMessage : Bool = false
    
    @State var shouldShowPopup : Bool = false
    
    func createBottomSolidMessage() -> some View {
        HStack(alignment: .center,spacing: 10){
                            
            Image(systemName: "book.fill")
                .font(.system(size: 40))
                .foregroundColor(Color.white)
//                        .background(Color.yellow)
            VStack(alignment: .leading, spacing: 0){
                Text("์•ˆ๋‚ด ๋ฉ”์„ธ์ง€")
                    .fontWeight(.black)
                    .foregroundColor(Color.white)
                
                Text("ํ† ์ŠคํŠธ ๋ฉ”์„ธ์ง€ ์ž…๋‹ˆ๋‹ค!asdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf")
                    .lineLimit(2)
                    .font(.system(size: 14))
                    .foregroundColor(Color.white)
                // Divider์˜ ์—ญํ• ์€ ํ…์ŠคํŠธ๊ฐ€ ๊ธธ์–ด์ ธ๋„ ์ด๋ฏธ์ง€๊ฐ€ ์ค‘์•™์— ์ •๋ ฌ๋˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•จ.
                Divider().opacity(0) 
            }
//                    .background(Color.red)
        }
        .padding(.vertical, 5)
        .padding(.horizontal, 10)
        .frame(width: UIScreen.main.bounds.width)
        // ๋…ธ์น˜์˜ ์—ฌ๋ถ€์— ๋”ฐ๋ผ์„œ safeArea๋ฅผ ์ •ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์—
        .padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom == 0 ? 0 : 15)
        .background(Color.purple)
    }
    
    func createTopSolidMessage() -> some View {
            HStack(alignment: .center,spacing: 10){
                                
                Image("jeongDaeRi")
                    .resizable()
                    .aspectRatio(contentMode: ContentMode.fill)
                    .frame(width: 60, height: 60)
                    .cornerRadius(10)
                VStack(alignment: .leading, spacing: 5){
                    Text("๊ฐœ๋ฐœํ•˜๋Š” ์ •๋Œ€๋ฆฌ๋‹˜์˜ ๋ฉ”์„ธ์ง€")
                        .fontWeight(.black)
                        .foregroundColor(Color.white)
                    
                    Text("์•ˆ๋…•ํ•˜์„ธ์š” ์˜ค๋Š˜๋„ ๋นก์ฝ”๋”ฉํ•˜๊ณ  ๊ณ„์‹ ๊ฐ€์š”? \n์˜ค๋Š˜์€ ํ† ์ŠคํŠธ ๋ฉ”์„ธ์ง€์™€ ํŒ์—…์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!")
                        .font(.system(size: 14))
                        .foregroundColor(Color.white)
                        Divider().opacity(0)
                }
    //                    .background(Color.red)
            }
            .padding(.vertical, 5)
            .padding(.horizontal, 10)
            .frame(width: UIScreen.main.bounds.width)
            .padding(.top, UIApplication.shared.windows.first?.safeAreaInsets.bottom == 0 ? 20 : 35)
            .background(Color.blue)
        }
    
    func createBottomToastMessage() -> some View {
        HStack(alignment: .top,spacing: 10){
                                    
                    Image("cat")
                        .resizable()
                        .aspectRatio(contentMode: ContentMode.fill)
                        .offset(y: 10)
                        .frame(width: 60, height: 60)
                        .cornerRadius(10)
                        
        //                        .background(Color.yellow)
                    VStack(alignment: .leading){
                        Text("๋‚ด ๊ณ ์–‘์ด")
                            .fontWeight(.black)
                            .foregroundColor(Color.white)
                        
                        Text("ํ† ์ŠคํŠธ ๋ฉ”์„ธ์ง€ ์ž…๋‹ˆ๋‹ค!asdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf")
                            
                            .font(.system(size: 14))
                            .foregroundColor(Color.white)
                        Divider().opacity(0)
                    }
        //                    .background(Color.red)
                }
                .padding(15)
                .frame(width: 300)
                .background(Color.green)
                .cornerRadius(20)
                
        
    }
    
    func createTopToastMessage() -> some View {
        HStack(alignment: .center,spacing: 10){
                                    
                    Image(systemName: "paperplane.fill")
                        .font(.system(size: 25))
                        .padding(.leading, 5)
                        .foregroundColor(Color.white)
                    VStack(alignment: .leading, spacing: 2){
                        Text("์ •๋Œ€๋ฆฌ๋‹˜์˜ ๋ฉ”์„ธ์ง€")
                            .fontWeight(.black)
                            .foregroundColor(Color.white)
                        Text("์˜ค๋Š˜๋„ ๋นก์ฝ”๋”ฉํ•˜๊ณ  ๊ณ„์‹ ๊ฐ€์š”?")
                            .font(.system(size: 14))
                            .lineLimit(1)
                            .foregroundColor(Color.white)
                    }
                    .padding(.trailing, 15)
        //                    .background(Color.red)
                }
                .padding(.vertical, 5)
                .padding(.horizontal, 10)
                .background(Color.orange)
                .cornerRadius(25)
                .padding(.top, UIApplication.shared.windows.first?.safeAreaInsets.bottom == 0 ? 0 : 30)
        
                    
    }
    
    func createPopup() -> some View {
        VStack(spacing: 10) {
                    Image("jeongDaeRi")
                        .resizable()
                        .aspectRatio(contentMode: ContentMode.fit)
                        .frame(width: 100, height: 100)

                    Text("๊ฐœ๋ฐœํ•˜๋Š” ์ •๋Œ€๋ฆฌ")
                        .foregroundColor(.white)
                        .fontWeight(.bold)

                    Text("ํ•œ๊ตญ์—์„œ ๊ฐœ๋ฐœ์ž๋กœ ์‚ด์•„๋‚จ๊ธฐ!\n์˜ˆ์ „์— ์ €์ฒ˜๋Ÿผ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๋˜๊ณ  ์‹ถ์ง€๋งŒ\n๊ทธ ๊ธธ์„ ๋ชฐ๋ผ ํ•ด๋งค๋Š” ๋ถ„๋“ค์—๊ฒŒ ๋„์›€ ๋˜๊ณ ์ž\n์ด ์ฑ„๋„์„ ์šด์˜ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.\n\nํ”„๋กœ๊ทธ๋žจ์— ๊ด€์‹ฌ ์žˆ๋Š” ๋ถ„๋“ค์ด๋‚˜ ์ทจ์—… ์ค€๋น„์ƒ ๋ถ„๋“ค๊ป˜\n์กฐ๊ธˆ์ด๋‚˜๋งˆ ๋„์›€์ด ๋˜์—ˆ์œผ๋ฉด ํ•ฉ๋‹ˆ๋‹ค.\n์•„์ž ์•„์ž ํ™”์ดํŒ…!๐Ÿ˜๐Ÿ‘")
                        .font(.system(size: 12))
                        .foregroundColor(Color(red: 0.9, green: 0.9, blue: 0.9))
                        .multilineTextAlignment(.center)

                    Spacer().frame(height: 10) // Spacer์—๋„ frame ์ง€์ • ๊ฐ€๋Šฅ

                    Button(action: {
                        self.shouldShowPopup = false
                    }) {
                        Text("๋‹ซ๊ธฐ")
                            .font(.system(size: 14))
                            .foregroundColor(.black)
                            .fontWeight(.bold)
                    }
                    .frame(width: 100, height: 40)
                    .background(Color.white)
                    .cornerRadius(20.0)
                }
        //        .padding(EdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20))
                    .padding(.horizontal, 10)
                .frame(width: 300, height: 400)
                .background(Color(hexcode: "8227b0"))
                .cornerRadius(10.0)
                .shadow(radius: 10)
    }
    
    var body: some View {
        ZStack{
            VStack(spacing: 10){
                
                Button(action: {
                    self.shouldShowBottomSolidMessage = true
                }, label: {
                    Text("๋ฐ”ํ…€์†”๋ฆฌ๋“œ๋ฉ”์„ธ์ง€")
                        .font(.system(size: 25))
                        .fontWeight(.bold)
                        .foregroundColor(Color.white)
                        .padding()
                        .background(Color.purple)
                        .cornerRadius(10)
                })

                Button(action: {
                    self.shouldShowBottomToastMessage = true
                }, label: {
                    Text("๋ฐ”ํ…€ํ† ์ŠคํŠธ๋ฉ”์„ธ์ง€")
                        .font(.system(size: 25))
                        .fontWeight(.bold)
                        .foregroundColor(Color.white)
                        .padding()
                        .background(Color.green)
                        .cornerRadius(10)
                })

                Button(action: {
                       self.shouldShowTopSolidMessage = true
                   }, label: {
                       Text("ํƒ‘์†”๋ฆฌ๋“œ๋ฉ”์„ธ์ง€")
                           .font(.system(size: 25))
                           .fontWeight(.bold)
                           .foregroundColor(Color.white)
                           .padding()
                           .background(Color.blue)
                           .cornerRadius(10)
                   })

                Button(action: {
                    self.shouldShowTopToastMessage = true
                }, label: {
                    Text("ํƒ‘ํ† ์ŠคํŠธ๋ฉ”์„ธ์ง€")
                        .font(.system(size: 25))
                        .fontWeight(.bold)
                        .foregroundColor(Color.white)
                        .padding()
                        .background(Color.orange)
                        .cornerRadius(10)
                })

                Button(action: {
                    self.shouldShowPopup = true
                }, label: {
                    Text("ํŒ์—…")
                        .font(.system(size: 25))
                        .fontWeight(.bold)
                        .foregroundColor(Color.white)
                        .padding()
                        .background(Color(hexcode: "8227b0"))
                        .cornerRadius(10)
                })

            } // Vstack
        } // Zstack
        // popup์˜ ๊ฒฝ์šฐ ์˜คํ”ˆ์†Œ์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ๋“ค
            .edgesIgnoringSafeArea(.all)
            .popup(
            	isPresented: $shouldShowBottomSolidMessage, // ๋‚˜ํƒ€๋‚˜๋Š” ์กฐ๊ฑด binding
                type: .toast, // ํƒ€์ž…์€ ํ† ์ŠคํŠธ๋กœ
                position: .bottom, // ์•„๋ž˜์—์„œ
                animation: .easeInOut, // ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ
                autohideIn: 2, // 2์ดˆ๊ฐ„ ์ง€์†
                closeOnTap: true, // ๋‚ด๋ถ€ ์˜์—ญ ํƒญํ•˜๋ฉด ์‚ฌ๋ผ์ง
                closeOnTapOutside: true, // ๋ฐ”๊นฅ ์˜์—ญ ํƒญํ•˜๋ฉด ์‚ฌ๋ผ์ง
                view: { // ์šฐ๋ฆฌ๊ฐ€ ์ง€์ •ํ•œ ๋ทฐ - ํด๋กœ์ €๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
                self.createBottomSolidMessage()
            })
            .popup(
            	isPresented: $shouldShowBottomToastMessage,
            	type: .floater(verticalPadding: 20), // ํ”Œ๋กœํŒ… ํƒ€์ž…์€ ์œ„์— ๋”ฑ ๋ถ™์–ด์„œ!
                position: .bottom,
                animation: .spring(),
                autohideIn: 2,
                closeOnTap: true,
                closeOnTapOutside: true,
                view: {
                self.createBottomToastMessage()
            })
            .popup(isPresented: $shouldShowTopSolidMessage, type: .toast, position: .top, animation: .easeInOut, autohideIn: 2, closeOnTap: true, closeOnTapOutside: true, view: {
                self.createTopSolidMessage()
            })
            .popup(isPresented: $shouldShowTopToastMessage, type: .floater(verticalPadding: 20), position: .top, animation: .spring(), autohideIn: 2, closeOnTap: true, closeOnTapOutside: true, view: {
                self.createTopToastMessage()
            })
            .popup(isPresented: $shouldShowPopup, type: .default, position: .bottom, animation: .spring(), closeOnTap: false, closeOnTapOutside: false, view: {
                self.createPopup()
            })
        
        
    }
}

 

โœ… ์˜คํ”ˆ์†Œ์Šค์—์„œ .popup์ชฝ์„ ์‚ดํŽด๋ณด์ž.

popup์ชฝ ์ฃผ์„์„ ๋ณด๋ฉด ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ type ๋ถ€๋ถ„์— ๋Œ€ํ•œ ์„ค๋ช…์„ ์ฒจ๊ฐ€ํ•˜์ž๋ฉด

float์€ ์ง€์ •ํ•œ position์— ๋”ฑ ๋ถ™์–ด์„œ ๋‚˜ํƒ€๋‚œ๋‹ค.

toast๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋‚˜ํƒ€๋‚œ๋‹ค.

 

 

 

 

๊ฒฐ๊ณผ๋ฌผ
๊ฒฐ๊ณผ๋ฌผ

'apple > ๐Ÿš SwiftUI & Combine' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[SwiftUI] menu (feat. Picker)  (0) 2022.05.25
[SwiftUI] Picker, segmentedStyle (feat. enum CaseIterable)  (0) 2022.05.23
[SwiftUI] TextField, SecureField  (0) 2022.05.23
[SwiftUI] ButtonStyle  (0) 2022.05.23
[SwiftUI] QRcodeReader  (0) 2022.05.23
Comments