Today Extension

Today Extension(也叫Widget)
这里写图片描述

Today Extension创建步骤

开始之前先要创建一个iOS项目,因为Extension不能脱离containing app而存在。本项目实例名为,TodayExtensionDemo,项目创建完后
具体步骤如下:

File -> New -> Target 选择Today Extension,点击继续。
这里写图片描述
这里写图片描述

添加后,设置Active Scheme为刚创建的Extension,如下图
这里写图片描述
点击运行,点击运行后会出现一个选择框,选择Today就可以了
这里写图片描述

然后下拉通知栏,点击今天,点击编辑,就看到可以添加刚刚自己创建的扩展了,扩展显示的名字可以在刚刚的info.plist中修改。
这里写图片描述
调试扩展的方式有两种:
1、选择这个扩展的scheme直接运行
2、先运行容器App,然后下拉通知栏打开今日扩展,点击Xcode菜单的Debug->Attach To Process ,选择Likly Targets中需要调试的扩展

代码编写

接下来的工作就是自定义这个展示页面了,如果你习惯使用Storyboard直接在MainInterface.storyboard上修改即可,如果你习惯自己Coding,你需要先修改一下info.plist
修改方法:

// 删除
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>

// 添加:
<key>NSExtensionPrincipalClass</key>
<string>TodayViewController</string>

//修改CFBundleDisplayName,调整Widget显示的名称
<key>CFBundleDisplayName</key>
<string>显示的名称</string>

1.调整Widget的高度

-(void)awakeFromNib {
    [super awakeFromNib];
    [self setPreferredContentSize:CGSizeMake(0, 240)];
}

取消widget默认的inset,让应用靠左

- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets
{
    return UIEdgeInsetsZero;
}

2.如果你要访问http站点链接,iOS9之后因为苹果App Transport Security (ATS)新特性,无法直接访问http数据,你也需要在Extension的plist中添加如下代码:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

3.因为Extension和containing app无法进行数据和文件共享,所以你还需要在Extenison中再添加一遍需要的文件。

4.在widget想要点击页面打开containing app。需要采用Open URL的方式打开containing app。

扫描二维码关注公众号,回复: 2770606 查看本文章

首先,在containing app的info.plist添加如下代码:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <!--这个一定要唯一-->
        <string>com.wildcat.TodayExtensionDemo</string>  
        <key>CFBundleURLSchemes</key>
        <array>
            <!--调转URL的host,例如:TodayDemo:// --->
            <string>TodayDemo</string>                    
        </array>
    </dict>
</array>

在today extension中实现:

-(void)openURLContainingAPP
{
    [self.extensionContext openURL:[NSURL URLWithString:@"lecoding://action=GotoHomePage"]
                 completionHandler:^(BOOL success) {
                     NSLog(@"open url result:%d",success);
                 }];

}

在 containing app appdelegate中添加如代码,接收跳转:

-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options{
    NSString* prefix = @"TodayDemo://";
    if ([[url absoluteString] rangeOfString:prefix].location != NSNotFound) {
        NSString* action = [[url absoluteString] substringFromIndex:prefix.length];
        if ([action isEqualToString:@"GotoHomePage"]) {

        }

        else if([action isEqualToString:@"GotoOtherPage"]) {

         }
    }
    return YES;
}

如何使用containing app中的图片
虽然Extention无法和containing app 公用库文件,但是可以公用图片,方法就是:

左侧选中containing app的Assets.xcassets,在右侧File Inspector中的Target Membership勾选Extension项目名。如下图:

这里写图片描述

在应用和扩展间共享数据 - App Groups

对 iOS 开发者来说,沙盒限制了我们在设备上随意读取和写入。但是对于应用和其对应的扩展来说,Apple 在 iOS 8 +中为我们提供了一种可能性,那就是 App Groups。App Groups 为同一个开发商的应用或者扩展定义了一组域,在这个域中同一个 group 可以共享一些资源。

首先我们需要开启 App Groups。选择主 Target,打开它的 Capabilities 选项卡,找到 App Groups 并打开开关,然后添加一个你能记得的 group 名字,比如group.myWidget。接下来你还需要为xxxWidget这个 Target 进行同样的配置,只不过不再需要新建 group,而是勾选刚才创建的 group 就行。
这里写图片描述
这里写图片描述


//读写共享的数据
- (NSUserDefaults *)loadGroupData
{
    NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.myWidget"];// SuiteName必须和上面Capabilities配置填写的一致
    return userDefault;
}

- (void)setVolume:(uint32_t)volume
{
    [[self loadGroupData] setObject:@(volume) forKey:@"volume"];
    [[self loadGroupData] synchronize];
}

- (uint32_t)getVolume:(uint32_t)volume
{
    NSNumber *volumeNumber = [[self loadGroupData] objectForKey:@"volume"];
    if (volumeNumber && [volumeNumber isKindOfClass:[NSNumber class]]) {
        return volumeNumber.unsignedIntValue;
    } else {
        return kDefaultVolume;
    }
}

widget向containing app 发送数据

//1、容器App设置定时器去轮询共享数据,一旦发生变化,就相应地改变音量。
//2、扩展发送请求到服务器,服务器再通知容器App。

//3.它就是 CFNotificationCenterGetDarwinNotifyCenter!这是CoreFoundation库中一个系统级的通知中心,苹果的系统自己也在用它,看清了“Darwin””了没有?哈哈!看了下CFNotificationCenter相关的API,跟NSNotificationCenter有点像。需要用到Toll-Bridge的知识与CoreFoundation相关的类进行桥接,这虽不常用但也不难。还需要注意下个别参数的使用。
// 接受不到数据,扩展发送通知前先存储数据,容器App接收到通知后,再读取共享数据那就好了
Containing App:

// 添加监听
- (void)addObserver
{
    CFNotificationCenterRef notification = CFNotificationCenterGetDarwinNotifyCenter ();
    CFNotificationCenterAddObserver(notification, (__bridge const void *)(self), observerMethod, CFSTR("通知名"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
}

// 监听widget改变
void observerMethod (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    //监听到notification后要做的处理
}

// 移除监听
- (void)removeObserver
{
    CFNotificationCenterRef notification = CFNotificationCenterGetDarwinNotifyCenter ();
    CFNotificationCenterRemoveObserver(notification, (__bridge const void *)(self), CFSTR("通知名"), NULL);
}

Widget:

// 发送通知
- (void)postNotificaiton
{
    CFNotificationCenterRef notification = CFNotificationCenterGetDarwinNotifyCenter ();
    CFNotificationCenterPostNotification(notification, CFSTR("通知名"), NULL, NULL, YES);
}

BTW,如果想实现更复杂的功能,推荐使用MMWormhole这个开源库,它专门用于在Container app 与 Extension间传递消息,苹果婊 Watch OS 也适用~
最后分享一个Podfile多个target引用部分相同pod库的编写方法:

def host_pods
pod 'SSKeychain', '~> 0.1.4'
pod 'INAppStoreWindow', :head
pod 'AFNetworking', '1.1.0'
end

def shared_pods
pod 'MMWormhole','~> 2.0.0'
end

target 'HostApp' do
shared_pods
host_pods
end

target 'Extension' do
shared_pods
end

代码期待中:https://github.com/JolieYa/BYTodayExtension

猜你喜欢

转载自blog.csdn.net/weixin_40873814/article/details/78957489