Widget的简单应用并适配iOS10

widget这个小插件不知道有多少人习惯使用?又或者有多少使用iphone手机的用户知道这东西的存在?好了,不说废话了;既然公司有这需求,小编也只能去研究了!下面来和大家介绍小编研究成果!查询了网上的相关内容,没有发现什么特别全面详细的文章!只能自己查官方文档喽!
官方对widget的解释:App Extension Programming Guide: Today

People view Today widgets in the Today area of Notification Center. Because people configure the Today area so that it displays the information they value most, it works well to approach the design of your widget with the goal of earning a place among the user’s most important items.

extension是iOS8新开放的一种对几个固定系统区域的扩展机制,extension并不是一个独立的app,它有一个包含在app bundle中的独立bundle,extension的bundle后缀名是.appex;需要依赖于containning app。


iOS 10 widget

点击“编辑”可是添加其他app的widget。

如何创建widget?

  • 创建一个工程,在该工程里添加targets:


    创建widget
  • 创建成功之后的项目结构:


    项目结构
  • iOS 10和iOS 10之前的界面对比:
iOS 10
iOS 10 之前

搭建简单的交互界面

  • 文件配置:
    系统生成的info.plist文件默认是使用Storyboard 实现的界面;如果你想使用代码实现是界面的搭建,需更改这个配置文件:

The Xcode Today template provides default header and implementation files for the principal class (named TodayViewController), an Info.plist file, and an interface file (that is, a storyboard or xib file).
By default, the Today template supplies the following Info.plist keys and values (shown here for an OS X target):
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widget-extension</string>
<key>NSExtensionPrincipalClass</key>
<string>TodayViewController</string>
</dict>
If you use a custom view controller subclass, use the custom class name to replace the TodayViewController value for the NSExtensionPrincipalClass key.

  • NSExtensionMainStoryboard:MainInterface(你Storyboard的名称)


  • NSExtensionPrincipalClass:TodayViewController(你widget控制器的名称)


NSExtensionAttributes:这是一个描述扩展点具体属性的字典,就像照片编辑扩展中的PHSupportedMediaTypes一样。
NSExtensionPrincipalClass:这是扩展模板创建的主体视图控制器类,比如TodayViewController。当载体应用程序(host app)调用扩展时,扩展点会实例化这个类。
NSExtensionMainStoryboard(只适用于iOS):扩展默认的Storyboard文件,一般名为MainInterface。

注:本文以代码为例!

方法实现:

  • 界面的搭建(<iOS 10):
-(void)viewDidLoad {
    [super viewDidLoad];
    WGHeaderView *headerView = [[WGHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 110)];
    headerView.delegate_ = self;
    [self.view addSubview: headerView];
    self.headerView = headerView;
}
// 设置界面的高度
-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 110);
}
  • 更新widget视图:(demo目前只是按钮 不需要更新)
-(void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
//    NCUpdateResultNewData   新的内容需要重新绘制视图
//    NCUpdateResultNoData    部件不需要更新
//    NCUpdateResultFailed    更新过程中发生错误
    completionHandler(NCUpdateResultNoData);
}

运行程序:


运行效果图

因此还需要实现:

-(UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets{
    return UIEdgeInsetsMake(0, 10, 0, 10);
}

注意:该方法在iOS 10之后就被淘汰了!

  • 实现点击按钮跳转:
    配置URL schemes:



    在按钮点击的方法里实现:(此处小编用的代理)

    NSString *urlStr = [NSString stringWithFormat:@"medicalWdget://%li",index];
    
    NSURL *url = [NSURL URLWithString:urlStr];
    
    [self.extensionContext openURL:url completionHandler:^(BOOL success) {
    }];

目前就可以跳转到app了!当然是四个按钮,你需要再AppDelegate里面进行处理:

-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{

    if ([url.scheme isEqualToString:@"medicalWdget"]) {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"ExtenicationNotification" object:url.host];
        
    }
    return YES;
}
// ios9 之后
-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options{
    
    if ([url.scheme isEqualToString:@"medicalWdget"]) {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"ExtenicationNotification" object:url.host];
        
    }
![72BBF230-83EB-4650-BE27-EE0FBBAAD35F.png](http://upload-images.jianshu.io/upload_images/1109379-2530dc667076faee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    return YES;
}

然后处理相应的通知,判断url.host点击的第几个按钮!

适配iOS 10:

  • NCWidgetDisplayMode
    NCWidgetDisplayModeCompact, // Fixed height
    NCWidgetDisplayModeExpanded, // Variable height

需要再设置高度之前设置该属性;

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    if ([[UIDevice currentDevice].systemVersion doubleValue] >= 10.0) {
        self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeCompact;
    }
    self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 110);
}

模式改变后会执行下面这个方法:

-(void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize{
    if (activeDisplayMode == NCWidgetDisplayModeCompact) {
        
        NSLog(@"maxSize-%@",NSStringFromCGSize(maxSize));// maxSize-{359, 110}
    }else{
        NSLog(@"maxSize-%@",NSStringFromCGSize(maxSize));// maxSize-{359, 616}
    }
}

两种设计风格:


iPhone 7的界面效果
iPhone 6(iOS8.4)的界面效果

扩展:


注意:有人看到这种界面可能会想到tableview实现,开始小编也是这样想的。可是实现起来发现布局都是乱的(具体原因没有找出来,如哪位研究出来了,麻烦交流一下)!所以建议大家使用UIView就可以了!
网络请求数据:

@try {
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     
            NSMutableDictionary *param = [[NSMutableDictionary alloc] init];
            [param setObject:@"gettopics" forKey:@"action"];
            [param setObject:@"135644452322" forKey:@"EchoToken"];
            [param setValue:@"data" forKeyPath:@"type"];
            [param setValue:[NSString stringWithFormat:@"%i",widgetCount] forKeyPath:@"pagesize"];
            [param setValue:@"1" forKeyPath:@"pageindex"];
            [param setValue:@"" forKeyPath:@"username"];
            [param setValue:@"cF54141DC1FA8E736B45244428874CE46==" forKeyPath:@"token"];
            
            NSDictionary *headers = @{@"Content-Type":@"application/json; charset=utf-8",
                                      @"Accept":@"application/json"
                                      };
            
            NSURLSession *session = [NSURLSession sharedSession];
            
            NSURL *url = [NSURL URLWithString:[@"v1/api.ashx?action=" stringByAppendingString:@"gettopics"]];
            
            NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
            request.HTTPBody = [[NSString stringWithFormat:@"type=data&pagesize=%i&pageindex=1",widgetCount ] dataUsingEncoding:NSUTF8StringEncoding];
            request.allHTTPHeaderFields = headers;
            request.HTTPMethod = @"POST";
            NSError *error = nil;
            
            NSData *jsonData = [NSJSONSerialization dataWithJSONObject:param options:NSJSONWritingPrettyPrinted error:&error];
            request.HTTPBody = jsonData;
            
            
            NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                
                NSError *jsonError = nil;
                NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
                if (!jsonError) {
                    NSMutableArray *mutArr = [NSMutableArray array];
                    for (NSDictionary *dict in jsonDict[@"list"]) {
                        
                        WGTopic *topic = [[WGTopic alloc] initWithDict:dict];
                        [mutArr addObject:topic];
                    }
                    self.hotTopicArr = mutArr;
                }
            }];
            // 启动任务
            [dataTask resume];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.view layoutIfNeeded];
            });
        });
    } @catch (NSException *exception) {
        
    } @finally {
        
    }

如果是http请求需要配置info.plist文件:


注意:从2017年1月起所有请求需要时https 的,否则审核就会被拒!web连接可以是http的,但是也需要配置!

APP数据互通:


通过一下沙盒存储方法存储数据和获取数据!

与app使用相同的方法文件:

将公共的文件打包成framework之后,进行相关的配置;

调试:

选择不同的项目运行,在相应的项目里的断点就会起作用!


本文demo

补充:widget的上线也是需要单独申请APP ID的 需要配置证书和Provisioning Profiles文件!version 和 build 的版本号一样要和主项目一样,否则你是不能上传到appstore进行发布的!


配置证书及描述文件:(列举一些)




证书与描述文件配置好之后:


感谢您的阅读,希望对您有所帮助!欢迎任何形式的转载,但请务必注明出处,尊重他人劳动!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容