环境: xcode10.1,iOS 12
效果图:
demo地址,欢迎star
1.创建Target
- File -> New ->target
-
创建完成之后的效果
-
默认使用Storyboard
2.Info.plist设置
如果想代码绘制UI,要进行以下操作
- Bundle display name:
Widget在通知栏显示的名称
- NSExtension
如果你是使用纯代码进行开发,请按照下面进行操作:
对于 OC
1)请删除NSExtensionMainStoryboard 的键值对和MainInterface.storyboard文件;
2)请添加NSExtensionPrincipalClass这个key,并将value设置为控制器(如TodayViewController)
对于 Swift
1)请删除NSExtensionMainStoryboard 的键值对和MainInterface.storyboard文件;
2)请添加NSExtensionPrincipalClass这个key,并将value设置为控制器(如$(PRODUCT_NAME).TodayViewController)
Swift有命名空间的问题,如果不加会报如下报错
2.Widget细节处理
iOS 8
-
- iOS8下没有折叠和展开功能,默认的Widget高度为self.preferredContentSize设置的高度。
preferredContentSize = CGSize(width: UIScreen.main.bounds.size.width, height: 110)
- 2.iOS8下所有组件默认右移30单位,可以通过下面的方法修改上下左右的距离
func widgetMarginInsets(forProposedMarginInsets defaultMarginInsets: UIEdgeInsets) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
iOS 10
iOS 10以后,有两种显示模式
NCWidgetDisplayModeCompact, // Fixed height,高度固定,最低高度为110
NCWidgetDisplayModeExpanded, // Variable height,高度可变
NCWidgetDisplayModeExpanded下高度一览表
. 修正:iPhoneX 和XS尺寸相同,但是最大的widget高度是不同的,上图上是模拟器的测试结果,XS的的最大高度应该是748。另外widget的最小高度也不一定是110(大部分情况下是),XS的高度是98,所以最好使用系统的方法动态的获取高度(extensionContext?.widgetMaximumSize(for: .compact).height)
- 在设置最大高度的时候要考虑到具体机型和系统
- Widget 的收起、展开 则是通过这个代理方法:
/**
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
if(activeDisplayMode == NCWidgetDisplayModeCompact) {
// 尺寸只设置高度即可,因为宽度是固定的,设置了也不会有效果
self.preferredContentSize = CGSizeMake(0, 110);
} else {
self.preferredContentSize = CGSizeMake(0, 310);
}
}
-
在设置 UI 的过程中,若想使用本体 Target 中的类:
3.数据共享
-
主App target设置Group
-
Widget target设置Group
同上
-
Userdefault共享数据
设置数据
//主项目设置widget数据(通过Userdefaults) AppDelegate.swift
private func setWidgetData() {
let userDafault = UserDefaults(suiteName: "group.tsc")
userDafault?.set("主App名字(Userdefaults)", forKey: "FirstDataName")
userDafault?.set("主App内容(Userdefaults)", forKey: "FirstDataContent")
userDafault?.synchronize()
}
取数据
//获取主项目设置widget数据(通过Userdefaults)TodayViewController.swift
func fetchWidgetData() -> WidgetModel {
let userDefault = UserDefaults(suiteName: "group.tsc")
let model = WidgetModel(name: userDefault?.string(forKey: "FirstDataName") ?? "未获从主App取到名字", date: "Yesterday", content: userDefault?.string(forKey: "FirstDataContent") ?? "未从主App获取到内容", imageName: "Report_Success_icon_120x120_")
return model
}
-
FileManager共享数据
设置数据
private var models: [WidgetModel] = {
let imageNameArray = ["Report_Success_icon_120x120_", "Report_Success_icon_120x120_", "Report_Success_icon_120x120_", "Report_Success_icon_120x120_", "Report_Success_icon_120x120_"]
let nameArray = ["Alex & Jessica(FileManager)", "Jessica(FileManager)", "Rocky(FileManager)", "Work Order(FileManager)", "Work Order(FileManager)"]
let dateArray = ["5:30 pm", "Yesterday", "11/02/2018", "11/02/2018", "11/02/2018"]
let conentArray = ["Rocky: Where are you Jessic?", "Rocky: Where are you Jessic?I will arrive station about 30n munites later", "Rocky: Where are you Jessic?I will arrive station about 30n munites later", "Rocky: Where are you Jessic?I will arrive station about 30n munites laterRocky: Where are you Jessic?I will arrive station about 30n munites later", "Rocky: Where are you Jessic?I will arrive station about 30n munites laterRocky: Where are you Jessic?I will arrive station about 30n munites later"]
var array = [WidgetModel]()
for i in 0..
取数据
//获取主项目设置widget数据(通过FileManager)
func setWidgetFileData() {
let manager = FileManager.default
var url = manager.containerURL(forSecurityApplicationGroupIdentifier: "group.tsc")
//路径上多了个file:// 要去掉
var urlString = url!.absoluteString.replacingOccurrences(of: "file://", with: "")
url?.appendPathComponent("Library/MainWidget/group.json")
urlString.append("Library/MainWidget/group.json")
//获取json数据
if manager.fileExists(atPath: urlString) {
do {
let json = try! String(contentsOf: url!)
let modelArray = try? JSONDecoder().decode([WidgetModel].self, from: json.data(using: .utf8)!)
models = modelArray ?? []
}
} else {
print("不存在")
}
}
4.Scheme配置
-
宿主App配置Scheme
-
Widget配置跳转
@objc func buttonDidClick(_ sender: UIButton) {
var action = ""
switch sender.tag {
case 100:
action = "scan"
case 101:
action = "pay"
case 102:
action = "rec"
case 103:
action = "zhuanzhang"
case 104:
action = "code"
default:
break
}
extensionContext?.open(URL(string: "MainWidgetScheme://action=\(action)")!, completionHandler: { (_) in
})
}
-
宿主App处理跳转
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let urlString = url.absoluteString
let startIndex = urlString.startIndex
let string = "MainWidgetScheme://action="
if urlString.contains(string) {
let newStartIndex = urlString.index(startIndex, offsetBy: string.count)
let code = urlString[newStartIndex..
demo地址,欢迎star
- 共用三方库 或者共用Framework可参考这个文章
Widget和主App共用Framework时出现的pod问题
Extension 证书配置