Today Extension
前言
简单介绍下APP Extension(应用扩展)中的Today Extension的使用方法。下图就是一个TodayExtension示例。
TodayExtension示例.PNG
实现Today Extension
创建Today Extension
创建TodayExtension.png有两种方式调出以上会话窗口:
1.点击工程 -> TARGETS -> "+" -> 选择Today Extension;
2.点击File -> New -> Target... -> 选择Today Extension。
工程中的Today Extension文件夹结构:
TodayExtension文件夹.png
代码实现
我一般习惯用Masonry布局,所以要删除MainInterface.storyboard,然后修改info.plist中的NSExtension:
NSExtension.png
下面是主要代码:
// TodayViewController.m中的代码
#import "TodayViewController.h"
#import <NotificationCenter/NotificationCenter.h>
#import <Masonry.h>
#import "TodayTableHeaderView.h"
@interface TodayViewController () <NCWidgetProviding, UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) UITableView *tableView;
@end
@implementation TodayViewController
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 将小组件的展现模式设置为可展开
if (@available(iOS 10.0, *)) {
self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
[self.view addSubview:self.tableView];
[_tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_offset(0);
}];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = @"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:identifier];
}
NSArray *titleArr = @[@"帮助",@"反馈",@"个人信息",@"客服",@"设置"];
cell.imageView.image = [UIImage imageNamed:titleArr[indexPath.row]];
cell.textLabel.text = titleArr[indexPath.row];
return cell;
}
#pragma mark - UITableViewDelegate
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *headerView = [[TodayTableHeaderView alloc]init];
return headerView;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *path = [NSString stringWithFormat:@"TodayExtension://%zd",indexPath.row];
NSURL *url = [NSURL URLWithString:path];
[self openContainingAPPWithURL:url];
}
- (void)openContainingAPPWithURL:(NSURL *)URL {
[self.extensionContext openURL:URL completionHandler:nil];
}
#pragma mark - NCWidgetProviding
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize API_AVAILABLE(ios(10.0)){
if (activeDisplayMode == NCWidgetDisplayModeExpanded) {
// 设置展开的新高度
self.preferredContentSize = CGSizeMake(0, 335.0);
}else{
self.preferredContentSize = maxSize;
}
}
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData
completionHandler(NCUpdateResultNewData);
}
#pragma mark - lazy load
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc]init];
_tableView.dataSource = self;
_tableView.delegate = self;
}
return _tableView;
}
@end
针对上述代码有两点需要说明:
-
"TodayExtension://"的作用:实现点击Today Extension时,通过openURL的方式主动调起Containing App(包含Today Extension的宿主应用),所以需要在Containing App中添加URL scheme;
URL scheme.png -
示例中Today Extension的展示内容是固定的,如果要求内容是由宿主应用决定的,那要怎么实现呢?通常做法是通过app group实现,即宿主应用往app group里写入内容,Today Extension去读取并展示。
// TodayTableHeaderView.m中的代码
#import "TodayTableHeaderView.h"
#import <Masonry.h>
@interface TodayTableHeaderView ()
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UILabel *label;
@end
@implementation TodayTableHeaderView
- (instancetype)init {
self = [super init];
if (self) {
[self addSubview:self.imageView];
[self addSubview:self.label];
// 布局子视图
[self mas_layoutSubviews];
}
return self;
}
- (void)mas_layoutSubviews {
[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.mas_offset(15.0);
make.bottom.mas_offset(-15.0);
make.height.mas_equalTo(80.0);
make.width.mas_equalTo(70.0);
}];
[self.label mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.imageView);
make.left.equalTo(self.imageView.mas_right).mas_offset(10.0);
}];
}
#pragma mark - lazy load
- (UIImageView *)imageView {
if (!_imageView) {
_imageView = [[UIImageView alloc]init];
_imageView.image = [UIImage imageNamed:@"son"];
_imageView.layer.cornerRadius = 8.0;
_imageView.layer.masksToBounds = YES;
}
return _imageView;
}
- (UILabel *)label {
if (!_label) {
_label = [[UILabel alloc]init];
_label.text = @"Hello World";
}
return _label;
}
@end
// 宿主应用的AppDelegate.m中的代码
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
// 处理跳转逻辑
NSString *absStr = url.absoluteString;
if ([absStr hasPrefix:@"TodayExtension"]) {
NSArray *titleArr = @[@"帮助",@"反馈",@"个人信息",@"客服",@"设置"];
NSInteger index = [[absStr substringFromIndex:absStr.length -1] integerValue];
HLog(@"用户点击了----%@",titleArr[index]);
}
return YES;
}
可能遇到的问题
1.Today Extension展示不出来
解决:检查下是否将Extension的deployment target设置的比宿主应用的deployment target高。如果是的话,将它设置的低于宿主应用。
2.在功能上,宿主应用和Extension是相互独立的,如果Extension想借用宿主应用的代码或者资源,那应该怎么办?
解决:选中要借用的资源,设置Target Membership就好了。
3.如果想与宿主应用共享三方框架,如masonry,那应该怎么办?
解决:在podfile里添加一个target,在target里写上你要用的三方框架。
target 'TodayExtension' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Pods for TodayExtension
pod 'Masonry'
end
参考文献
iOS - App Extension 整体总结
iOS应用扩展(APP Extension)- Today Extension使用
ios TodayExtension 在真机上不显示简单解决方案