上篇文章是對widget的整體調研,真正上項目會有很多坑
運行主項目或者單獨運行widget都可以使widget代碼生效
一.widget不走斷點
1.因為func getTimeline
方法不一定每次運行項目都會及時生效锌雀,有時widget界面刷新了迅诬,但是斷點卻沒走,升級xcode到12.2就好很多
2.在主項目主動調用widget刷新
WidgetCenter.shared.reloadAllTimelines()
需要注意的是這個方法需要import WidgetKit
惩歉,WidgetKit只有swift版本,所以主項目如果是OC需要混編調用swift
3.在主項目隨便打一個斷點(比如didFinishLaunch)上遥,因為主項目是走斷點的粉楚,運行下主項目亮垫,走斷點,運行主項目widget也會運行燃异,發(fā)現(xiàn)widget中的斷點也走了继蜡,神奇
4.老辦法:跑一遍主項目再跑widget項目,重新安裝APP鲫剿,clean Xcode稻轨,重啟Xcode,重啟電腦政冻,重啟手機
二.widget不刷新线欲,或者刷新不及時
我們都知道widget的刷新是受func getTimeline
控制的,因為這個方法受系統(tǒng)控制苦锨,系統(tǒng)會根據(jù)widget的使用頻率和類型趴泌,來決定widget的刷新頻率,所以func getTimeline
的刷新最早時間是我們手動寫的刷新策略秃励,很有可能刷新時間會延后
另外手動寫的刷新策略過于頻繁吉捶,會延后小組件的刷新
舉個??皆尔,策略為十分鐘以后刷新
let currentDate = Date()
let entryDate = Calendar.current.date(byAdding: .minute, value: 10, to: currentDate)!
let entry = SimpleEntry(date: entryDate)
var entries: [SimpleEntry] = []
entries.append(entry)
return Timeline(entries: entries, policy: .after(entryDate))
policy是刷新策略慷蠕,有三種方式
.atEnd,當前entries執(zhí)行完畢后砌们,立刻刷新
.after,設定時間刷新
.never,不刷新
entryDate是指定每個entry的刷新時間
雖然第一次添加widget搁进,func getTimeline
一定會走昔头,但是在有些機器不能及時的刷新,目前沒有找到解決方案莱革,猜測和系統(tǒng)刷新頻率有關
還可以像上面說的在主項目主動調用widget刷新WidgetCenter.shared.reloadAllTimelines()
讹开,比如在主項目切換到后臺時刷新
三.widget黑屏
1.不管是在添加widget界面還是把widget添加到桌面,黑屏可能widget已經(jīng)崩潰了闹击,單獨運行widget Extension查看是否崩潰成艘,
2.func getTimeline
方法沒有返回,數(shù)據(jù)錯誤断箫,網(wǎng)絡錯誤秋冰,都要返回func getTimeline
的completion
閉包
3.采用每個widget單獨加載一個widget樣式的方案,不要一個widget加載多個widget樣式埃撵,這樣在沒有加載出來widget時甥材,系統(tǒng)的占位文本不知道加載哪種widget樣式導致黑屏,具體加載方式看上一篇文章
4.有時是設備問題鸳惯,換臺設備試試
四.widget不停刷新
1.調用了持續(xù)定位
2.func getTimeline
方法中completion
閉包被頻繁調用,可能存在多個異步調用操作
比如報錯:
Terminating app due to uncaught exception 'NSFileHandleOperationException', reason: '*** -[NSConcreteFileHandle fileDescriptor]: Invalid argument'
原因:getTimeline的completion閉包只能調用一次
五.調用主項目绪商,三方庫
如果主項目是OC辅鲸,或者現(xiàn)在widget target項目里面加入OC文件,需要加入橋接文件例书,如果加入.intentdefinition的配置文件刻炒,需要在橋接文件中,引入#import "ConfigurationIntent.h"
树瞭,否則會報錯誤Cannot find type 'ConfigurationIntent' in scope爱谁。
如果需要用cocopod中的其他庫,需要添加新的target凉敲,加入在widget擴展中需要用到的庫捐顷,比如加入AFNetworking:
target 'WidgetExtension' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
pod 'AFNetworking'
# Pods for WidgetExtension
end
如果需要主項目中的文件,在需要用到的.m文件Target Membership中勾選widgetExtention废赞,這樣widget小組件才能用這個OC 文件叮姑。可能需要一些兼容性改動:引入一些缺少的頭文件耘沼,勾選其他關聯(lián)文件的Target Membership朱盐,添加widget關聯(lián)。勾選后編譯一下狂秘,沒問題后把用到的OC文件添加到橋接文件中聲明,這樣swiftUI才能調用原來OC主項目的文件
六.界面混編問題
widget界面不能用UIKit的任何東西破衔,遵守UIViewRepresentable協(xié)議橋接過來也不行钱烟,只能用swiftUI寫界面
七.不能高德定位
目前高德定位初始化[[AMapLocationManager alloc] init],會崩潰读第;報錯Assertion failure in -[CLLocationManager setPausesLocationUpdatesAutomatically:]
稻扬,但是系統(tǒng)定位可以拿到經(jīng)緯度羊瘩,再反地理位置編碼
需要在info.plist添加NSLocationAlwaysAndWhenInUseUsageDescription
,NSLocationAlwaysUsageDescription
,NSLocationUsageDescription
,NSLocationWhenInUseUsageDescription
,還需要添加NSWidgetWantsLocation
設為true逝她,允許小組件定位
八.機型適配睬捶,swiftUI布局
這是官方給的widget在各個機型的尺寸擒贸,然后我發(fā)現(xiàn)widget在各個機型尺寸都不一樣,和屏幕比例沒有關系徽惋,不成比例座韵,到屏幕邊緣的距離也各不相同,找不要系統(tǒng)方法誉碴,為了獲取widget的大小寬高,就根據(jù)這個表代咸,寫死了一套獲取代碼(不對成黄,下面有直接獲取方法)
enum WidgetSizeEnum {
case small
case medium
case large
}
let ScreenWidth = UIScreen.main.bounds.width
let ScreenHeight = UIScreen.main.bounds.height
func WidgetSize(_ widgetType:WidgetSizeEnum) -> CGSize {
if ScreenWidth == 320 && ScreenHeight == 568 {
switch widgetType {
case .small:
return CGSize(width: 141, height: 141)
case .medium:
return CGSize(width: 291, height: 141)
case .large:
return CGSize(width: 291, height: 299)
}
}
if ScreenWidth == 375 && ScreenHeight == 667 {
switch widgetType {
case .small:
return CGSize(width: 148, height: 148)
case .medium:
return CGSize(width: 322, height: 148)
case .large:
return CGSize(width: 322, height: 324)
}
}
if ScreenWidth == 414 && ScreenHeight == 736 {
switch widgetType {
case .small:
return CGSize(width: 159, height: 159)
case .medium:
return CGSize(width: 348, height: 159)
case .large:
return CGSize(width: 348, height: 357)
}
}
if ScreenWidth == 375 && ScreenHeight == 812 {
switch widgetType {
case .small:
return CGSize(width: 155, height: 155)
case .medium:
return CGSize(width: 329, height: 155)
case .large:
return CGSize(width: 329, height: 345)
}
}
if ScreenWidth == 414 && ScreenHeight == 896 {
switch widgetType {
case .small:
return CGSize(width: 169, height: 169)
case .medium:
return CGSize(width: 360, height: 169)
case .large:
return CGSize(width: 360, height: 376)
}
}
switch widgetType {
case .small:
return CGSize(width: 148, height: 148)
case .medium:
return CGSize(width: 322, height: 148)
case .large:
return CGSize(width: 322, height: 324)
}
}
let kSmallWidgetWidth = WidgetSize(.small).width
let kMediumWidgetWidth = WidgetSize(.medium).width
let kLargeWidgetWidth = WidgetSize(.large).width
let kSmallWidgetHeight = WidgetSize(.small).height
let kMediumWidgetHeight = WidgetSize(.medium).height
let kLargeWidgetHeight = WidgetSize(.large).height
后來發(fā)現(xiàn)有些機型沒有包含贩耐,比如iPhone12,min管搪,max,后來在翻了一遍WidgetKit庫終于找到了系統(tǒng)方法更鲁,哭了奇钞,上面寫的這一套是費代碼,坑媒至,
public struct TimelineProviderContext {
public let displaySize: CGSize
}
在widget三個方法placeholder
,getSnapshot
,getTimeline
都可以獲取
context.displaySize
swiftUI布局也有很多坑谷徙,下次另開一篇文章細說
比如
Image("imageName")
.resizable()
.frame(width: fitWidth(22), height: fitWidth(22))
圖片設置大小要先設置resizable,否則不生效
發(fā)現(xiàn)一個神坑谋旦,原來不帶編輯功能的小組件屈尼,更新版本到有編輯功能的小組件,原來添加的靜態(tài)不可編輯的小組件就不能用了甲捏,重啟手機就好了(但不能讓用戶重啟手機吧)涨椒,新添加的widget沒有問題桶现,分析原因是初始化方式改變了,StaticConfiguration變成了IntentConfiguration眉反,代碼沒更新猎提,所以開始就帶上可編輯功能吧