์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
- ios
- XCTest
- visionOS
- ๋ฐฑ์ค
- node.js
- MVVM
- TCA
- ํจ์คํธ์บ ํผ์ค
- tableView
- Swfit
- SwiftUI
- rxcocoa
- UIKit
- SnapKit
- BOJ
- Flutter
- combine
- raywenderlich
- designpattern
- Xcode
- CollectionView
- realm
- ํ๋ก๊ทธ๋๋จธ์ค
- reactorkit
- RxSwift
- Lv2
- swift
- BFS
- arkit
- Kuring
- Today
- Total
lgvv98
[SwiftUI] Toast, popup ๋ณธ๋ฌธ
Toast, popup
โ ์ด๋ฒ์๋ ์คํ์์ค๋ฅผ ํ์ฉํด์ toast์ popup์ ํ๋ ์์ ์ ์งํํด๋ณด์.
์ด์ ์ ํ๋ฌํฐ๋ ์๋๋ก์ด๋ ๊ฐ๋ฐํ ๋, ์ฃผ๋ก ์ฌ์ฉํ๋ UI์ธ๋ฐ, iOS์๋ ๋น์ทํ๊ฒ ๊ตฌํํ ์ ์๋ค๊ณ ํด์ ๊ณต๋ถํด ๋ณด์๋ค.
โ ์คํ์์ค
https://github.com/exyte/PopupView
์คํ์์ค์์ 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 |