最近使用SwiftUI做項(xiàng)目,鄙人沒(méi)有想到還會(huì)用它做項(xiàng)目,SwiftUI的優(yōu)勢(shì)不用多說(shuō),把開(kāi)發(fā)過(guò)程中遇到問(wèn)題歸納一下
一 給list加側(cè)滑
在iOS15中 使用 .swipeActions(edge: .trailing,allowsFullSwipe: false) 所有的層疊布局(overlay 和 backgroond 全部失效),。文字顏色,圖片大小 都無(wú)法修改。通過(guò)蘋(píng)果官方文檔只能實(shí)現(xiàn)如下:

而如果要實(shí)現(xiàn)這種該如何呢???

解決辦法: 自定義Drap手勢(shì),利用
ViewModifier進(jìn)行實(shí)現(xiàn)。
ViewModifier是SwiftUI中一個(gè)用于封裝和重用視圖修改邏輯的協(xié)議。通過(guò)實(shí)現(xiàn)這個(gè)協(xié)議,可以創(chuàng)建自定義的修改器(modifier),這樣就可以應(yīng)用于任何視圖,以改變其外觀、行為或其它屬性。使用ViewModifier可以方便地統(tǒng)一調(diào)整全局效果,提高代碼的可重用性和維護(hù)性。
二 給某個(gè)組件加部分圓角的方法
2.1 使用clipShape
Blur()
.clipShape(CustomRoundedRectangle(corners: [.bottomLeft,.bottomRight], radius: 24~))
2.2 使用 background
.background {
CustomRoundedRectangle(corners: [.bottomLeft,.bottomRight], radius: 24~)
}
使用background在某些視圖上設(shè)置圓角沒(méi)效果,比如這里的 Blur()組件
struct Blur: UIViewRepresentable {
let style: UIBlurEffect.Style = .dark
func makeUIView(context: Context) -> UIVisualEffectView {
return UIVisualEffectView(effect: UIBlurEffect(style: style))
}
func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
uiView.effect = UIBlurEffect(style: style)
}
}
struct CustomRoundedRectangle: Shape {
var corners: UIRectCorner
var radius: CGFloat
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}
}
三 Image的使用誤區(qū)
Image(“face_1”) face_1必須在Assets.xcassets的圖片
如果不在Assets.xcasse里面可以使用 Image(uiImage: UIImage(named: taskItem.taskEmoIcon ?? "") ?? UIImage())
Image(<#T##String#>, bundle: <#T##Bundle?#>) 它可以嗎 bundle 又該如何設(shè)置呢???
四 Accessing StateObject's object without being installed on a View. This will create a new instance each time
在init里面更新StateObject的值,不行的 。
https://stackoverflow.com/questions/68930434/accessing-stateobjects-object-without-being-installed-on-a-view-this-will-crea
錯(cuò)誤代碼:
@StateObject var tabBarViewModel :DailDeilTabBarViewModel = DailDeilTabBarViewModel()
init() {
DaiDelPushManger.manger.didReceivePushMsg = { targetContentIdentifier in
// 跳轉(zhuǎn)到首頁(yè)
if tabBarViewModel.tabBarIndex != 0 {
tabBarViewModel.tabBarIndex = 0
}
}
}
五 Swiftui 處理冷啟動(dòng)
struct DailyDelightApp: App 里面init方法并不能處理冷啟動(dòng)業(yè)務(wù)。
iOS使用 @UIApplicationDelegateAdaptor 裝飾器,來(lái)處理冷啟動(dòng)的相關(guān)業(yè)務(wù)。
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
struct DailyDelightApp: App {
let persistenceController = PersistenceController.shared
@StateObject var tabBarViewModel :DailDeilTabBarViewModel = DailDeilTabBarViewModel()
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
init() {
//appInit()
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 在這里處理冷啟動(dòng)邏輯
DebugPrint("應(yīng)用冷啟動(dòng)完成")
return true
}
}
6 Swiftui 類(lèi)中嵌套enum和Struct
SwiftUI 類(lèi)中確實(shí)可以嵌套枚舉。這種做法可以在類(lèi)內(nèi)部定義特定的枚舉類(lèi)型,以便專門(mén)用于該類(lèi)。這有助于組織相關(guān)的功能和數(shù)據(jù),使得代碼更加模塊化和易于維護(hù)。
萬(wàn)萬(wàn)沒(méi)想到,太high了?。。。。?/p>
class SubScribleViewModel:ObservableObject {
enum SubScribleType {
case Month
case FreeYear
}
}
7 Animation
7.1 timingCurve
在 SwiftUI 中,animation.timingCurve 是一種用于定義動(dòng)畫(huà)時(shí)序的方式,它通過(guò)貝塞爾曲線控制動(dòng)畫(huà)的速度變化。這種方法允許開(kāi)發(fā)者創(chuàng)建自定義的動(dòng)畫(huà)效果,使得動(dòng)畫(huà)過(guò)程更加生動(dòng)和自然。
timingCurve 是一種插值動(dòng)畫(huà),可以定義動(dòng)畫(huà)在不同時(shí)間點(diǎn)的速度變化。具體來(lái)說(shuō),它包含四個(gè)參數(shù),這些參數(shù)表示貝塞爾曲線控制點(diǎn)的位置:
animation(timingCurve: c1x, c1y, c2x, c2y)
c1x 和 c1y: 第一個(gè)控制點(diǎn)的x和y坐標(biāo)。
c2x 和 c2y: 第二個(gè)控制點(diǎn)的x和y坐標(biāo)。
Each(0..<5) { index in
Group {
Circle()
.frame(width: geometry.size.width / 5, height: geometry.size.height / 5)
.scaleEffect(calcScale(index: index))
.offset(y: calcYOffset(geometry))
}.frame(width: geometry.size.width, height: geometry.size.height)
.rotationEffect(!self.isAnimating ? .degrees(0) : .degrees(360))
.animation(Animation
.timingCurve(0.5, 0.15 + Double(index) / 5, 0.25, 1, duration: 1.5)
.repeatForever(autoreverses: false))
常見(jiàn)的easeInOut easeIn easeOuti其實(shí)系統(tǒng)給我們通過(guò)timingCurve封裝的特殊類(lèi)型,我們只是拿過(guò)來(lái)用而已。
8 SwiftUI 可以在主線程刷新UI,而不閃退
我們知道在UIKit中在子線程刷新UI,閃退是必然的。而SwiftUI 子線程刷新 UI 時(shí)不會(huì)閃退,因?yàn)?SwiftUI 中的視圖能夠直接在后臺(tái)線程中更新。但是,必須要保證 UI 的更新是在 UI 線程(也稱為主線程)中進(jìn)行的,這是由于 UIKit 和 SwiftUI 都是設(shè)計(jì)為在主線程上操作 UI 元素,以保證響應(yīng)性和流暢度。如果在子線程中直接進(jìn)行 UI 更新,應(yīng)用程序可能會(huì)出現(xiàn)閃退或其他未知行為。
這點(diǎn)確實(shí)出乎意料啊?。。?!
subScribleViewModel.goSubscriblePay{ message, succeed in
DispatchQueue.main.async {
subScribleViewModel.isShowLoading = false
showToast(msg: message)
if succeed {
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
isShowSubScribleAlert = false
})
}
}
}
9 GeometryReader
過(guò)度依賴 GeometryReader 會(huì)導(dǎo)致視圖布局變得僵化,失去了 SwiftUI 的靈活性優(yōu)勢(shì)。
GeometryReader 打破了 SwiftUI 聲明式編程的理念,使得需要直接操作視圖框架,更接近命令式編程。少用?。。?!
// SubscribeNow
// GeometryReader { render in
Text("SubscribeNow".getInternationalText(key: .SubscribeNowText))
.font(.system(size: 16~,weight: .medium))
.foregroundStyle(Color(hex: "#FFFFFFFF") ?? .black)
.padding(.vertical,18~)
.padding(.horizontal,116~)
.modifier(DailDelTypeCornerModifier(cornerRadius: 28~, fillColor: "#FF0A0F14"))
.padding(.top,subScribleViewModel.subScribleType == .Month ? 32~ : 16~)
.onTapGesture {
goSubsciblePay()
}
//}
用上后,布局就會(huì)混亂。
9 監(jiān)聽(tīng)app 進(jìn)入前后和后臺(tái)
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
DebugPrint("進(jìn)入后臺(tái)!")
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
DebugPrint("進(jìn)入前臺(tái)!")
isOpenPush(isOnReminder: true)
// 第二次如果沒(méi)授權(quán) 就不要彈窗了
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
isOpenPushPresented = false
})
}
10 鍵盤(pán)遮擋輸入框的解決防范
鍵盤(pán)遮擋是個(gè)非常頭疼的問(wèn)題,無(wú)論是uikit還是swifui,在swifui可以通過(guò)onReceive來(lái)監(jiān)聽(tīng)鍵盤(pán)活動(dòng):
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)) { notification in
guard let userInfo = notification.userInfo,
let keyboardEndFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {
return
}
let keyboardHeight = keyboardEndFrame.height
bottomPadding = keyboardHeight
}
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
bottomPadding = 0
}
10 error: Couldn't lookup symbols:
DailyDelight.DailDelHomePage.isCurrentDayTask.getter : Swift.Bool
Xcode 無(wú)法po SwiftUI 的裝飾器的變量
po self._someProperty而不是po self.someProperty。