欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS - Widget小部件(Today Extension)

程序员文章站 2022-03-11 10:57:10
...

Today Extension是iOS8新开放的一种对几个固定系统区域的扩展机制,它可以在一定程度上弥补iOS的沙盒机制对应用间通信的限制,iOS10之后又添加了一些功能。可能好多人不知道或不怎么用Widget。那到底什么小部件呢?看图:
iOS - Widget小部件(Today Extension)
看到这个图你应该就知道Widget这是什么了吧,可能你正在使用它。

之后我们研究下面几个问题:

1、为什么要使用widget;
2、如何在现有的工程添加widget;
3、如何试图布局;
4、如何调起主App以及进入相应的页面;
5、如何与主App共享数据。

为什么要使用widget呢?

它为我们的应用提供了一种便捷的服务方式,比如用户可以在Today Extension中查看应用展示的简略信息,而不用再进到我们的应用中,同样可以快捷操作app的功能,这将是一种全新的用户体验。重点就是便捷、快,这是苹果在iOS12中所追求的。

特点:Widget你可以看作是一个独立的小项目,当你添加好之后,你会发现它和主App不互通,想要很好的应用它,需要配置一些文件。如下面这些步骤:

如何在现有的工程添加widget

首先打开你的项目,在原有的工程基础上,想要使用Today Extension,我们需要创建一个新的target,点击File–>New–>Target–>Today Extention,如下图所示:
iOS - Widget小部件(Today Extension)
iOS - Widget小部件(Today Extension)
添加成功后项目的目录会如下图所示:
iOS - Widget小部件(Today Extension)
运行项目会看到如下图所示的效果:
iOS - Widget小部件(Today Extension)
注意:这里你运行的项目时会发现有两个可运行项目(Widget是独立的),当你运行主项目时,是无法调试Widget的,无法打断点调试,无法打印一些数据等。

如何试图布局

试图布局这里分两种,纯代码和storyboard,如果想用纯代码,需要在plist文件里配置一点东西:选择删除默认创建的MainInterface.storyboard,并在info.plist中删除NSExtensionMainStoryboard,添加NSExtensionPrincipalClass为TodayViewController。
iOS - Widget小部件(Today Extension)
设置视图的大小:

- (void)viewDidLoad {
    [super viewDidLoad];
    // iOS10,设置展开折叠
    self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
#pragma mark - NCWidgetProviding
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize NS_AVAILABLE_IOS(10_0){
    if (activeDisplayMode == NCWidgetDisplayModeCompact){
        // 折叠后默认高度110
        self.preferredContentSize = CGSizeMake(SCREEN_WIDTH, 110);
    }else{
        // 展开后的高度由你来定
        self.preferredContentSize = CGSizeMake(SCREEN_WIDTH, 485);
    }
}

注意:这里如果你想用masonry库来做约束,主App里Cocoapods导入的masonry是用不了的,你可以把masonry再导入你的 CNTodayViewController 里面,因为它是一个独立的项目,所以好多东西都需要你重新导入,但是Widget讲的是便捷,不会有太复杂的布局,一般就几个按钮,调起主App页面用的,像QQ音乐就三个按钮,很简洁。

如何调起主App以及进入相应的页面

Today Extension只能通过openURL的方式来调起app,并且需要在info.plist文件中配置参数URL types。

主App工程中配置:
iOS - Widget小部件(Today Extension)
Today Extension配置:
iOS - Widget小部件(Today Extension)
URL identifier为app的bundle ID,URL Schemes配置为app的scheme

#pragma mark - Widget 通过openURL的方式启动Containing APP
- (void)openURLContainingAPP{
    //scheme为app的scheme
    [self.extensionContext openURL:[NSURL URLWithString:@"CNCrm://"]
                 completionHandler:^(BOOL success) {
                     NSLog(@"open url result:%d",success);
                 }];
}
#pragma mark - Widget 进入主App不同的页面
- (void)openDiffirentPage{
[self.extensionContext openURL:[NSURL URLWithString:@"CNCrm://Action=HomePage"]
                     completionHandler:^(BOOL success) {
                         NSLog(@"open url result:%d",success);
                     }];
[self.extensionContext openURL:[NSURL URLWithString:@"CNCrm://Action=DetailPage"]
                     completionHandler:^(BOOL success) {
                         NSLog(@"open url result:%d",success);
                     }];
[self.extensionContext openURL:[NSURL URLWithString:@"CNCrm://Action=MyPage"]
                     completionHandler:^(BOOL success) {
                         NSLog(@"open url result:%d",success);
                     }];
 }

在主App的AppDelegate.m里面:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options NS_AVAILABLE_IOS(9_0){
     NSString* prefix = @"CNCrm://Action=";
        if ([[url absoluteString] rangeOfString:prefix].location!=NSNotFound) {
            NSString *action = [[url absoluteString] substringFromIndex:prefix.length];
            if ([action isEqualToString:@"HomePage"]) {
            // 自行处理事件
            } else if ([action isEqualToString:@"DetailPage"]){
            // 自行处理事件
            } else if ([action isEqualToString:@"MyPage"]){
            // 自行处理事件
            }
        }
    return YES;
}

如何与主App共享数据

首先需要去苹果开发者中心的APP Groups中创建一个APP Group,命名方式”group.com.companyName.xxx”,如下图:
iOS - Widget小部件(Today Extension)
创建好Group ID之后,回到项目里面配置(主App和Today Extension都要配置):

主App和Today Extension的配置都如下图所示:
iOS - Widget小部件(Today Extension)

通过App Groups提供的同一group内app共同读写区域,可以用NSUserDefaults和NSFileManager两种方式实现Today Extension和containing app之间的数据共享。

通过NSUserDefaults共享数据

- (void)saveDataByNSUserDefaults
{
    NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.xxx.xxx"];
    [shared setObject:@"哈哈" forKey:@"Widget"];
    [shared synchronize];
}
- (NSString *)readDataFromNSUserDefaults
{
    NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.xxx.xxx"];
    NSString *value = [shared valueForKey:@"Widget"];

    return value;
}

通过NSFileManager共享数据

- (BOOL)saveDataByNSFileManager
{
    NSError *error = nil;
    NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.xxx.xxx"];
    containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];

    NSString *value = @"test";
    BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&error];
    if (!result) {
        NSLog(@"%@",error);
    } else {
        NSLog(@"save value:%@ success.",value);
    }

    return result;
}
- (NSString *)readDataByNSFileManager
{
    NSError *error = nil;
    NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.xxx.xxx"];
    containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];
    NSString *value = [NSString stringWithContentsOfURL:containerURL encoding:NSUTF8StringEncoding error:&error];

    return value;
}

通过这两个方法就可以实现了Today Extension与主App的数据共享,类似于主App里面的数据存取。

相关标签: widget 小部件