Widget的简单应用并适配iOS10

cn075361 7年前
   <p>widget这个小插件不知道有多少人习惯使用?又或者有多少使用iphone手机的用户知道这东西的存在?好了,不说废话了;既然公司有这需求,小编也只能去研究了!下面来和大家介绍小编研究成果!查询了网上的相关内容,没有发现什么特别全面详细的文章!只能自己查官方文档喽!</p>    <p>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.</p>    <p>extension是iOS8新开放的一种对几个固定系统区域的扩展机制,extension并不是一个独立的app,它有一个包含在app bundle中的独立bundle,extension的bundle后缀名是.appex;需要依赖于containning app。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1d33abfc8fcfcb5e19d77c923a86f1c3.png"></p>    <p style="text-align:center">iOS 10 widget</p>    <p>点击“编辑”可是添加其他app的widget。</p>    <p><strong>如何创建widget?</strong></p>    <ul>     <li> <p>创建一个工程,在该工程里添加targets:</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a446a43cf980045c253ad1eb1ca98fb6.png"></p>    <p style="text-align:center">创建widget</p>    <ul>     <li> <p>创建成功之后的项目结构:</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/64d165336932a8479f3842ec30fd871a.png"></p>    <p>项目结构</p>    <ul>     <li> <p>iOS 10和iOS 10之前的界面对比:</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c44f25e3690e505454a6356fa68e936b.png"></p>    <p style="text-align:center">iOS 10</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/87c62f58ac8ebca8afb18841eb13f67c.png"></p>    <p><strong>搭建简单的交互界面</strong></p>    <ul>     <li> <p>文件配置:</p> </li>    </ul>    <p>系统生成的info.plist文件默认是使用Storyboard 实现的界面;如果你想使用代码实现是界面的搭建,需更改这个配置文件:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b6a3afc47495a19f575bf11d8c018281.png"></p>    <ul>     <li> <p>NSExtensionMainStoryboard:MainInterface(你Storyboard的名称)</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/02cbfde33eee706c91640545f27c2ea0.png"></p>    <ul>     <li> <p>NSExtensionPrincipalClass:TodayViewController(你widget控制器的名称)</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a2ec84fadbb251c6691a8f7fc3a4dcbb.png"></p>    <p>NSExtensionAttributes:这是一个描述扩展点具体属性的字典,就像照片编辑扩展中的PHSupportedMediaTypes一样。</p>    <p>NSExtensionPrincipalClass:这是扩展模板创建的主体视图控制器类,比如TodayViewController。当载体应用程序(host app)调用扩展时,扩展点会实例化这个类。</p>    <p>NSExtensionMainStoryboard(只适用于iOS):扩展默认的Storyboard文件,一般名为MainInterface。</p>    <p>注:本文以代码为例!</p>    <p><strong>方法实现:</strong></p>    <p>界面的搭建(</p>    <pre>  <code class="language-objectivec">-(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);  }</code></pre>    <p>更新widget视图:(demo目前只是按钮  不需要更新)</p>    <pre>  <code class="language-objectivec">-(void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {  //    NCUpdateResultNewData   新的内容需要重新绘制视图  //    NCUpdateResultNoData    部件不需要更新  //    NCUpdateResultFailed    更新过程中发生错误    completionHandler(NCUpdateResultNoData);  }</code></pre>    <p>运行程序:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d9141194dee53950855bad5d24c51958.png"></p>    <p style="text-align:center">运行效果图</p>    <p>因此还需要实现:</p>    <pre>  <code class="language-objectivec">-(UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets{    return UIEdgeInsetsMake(0, 10, 0, 10);  }</code></pre>    <p>注意:该方法在iOS 10之后就被淘汰了!</p>    <p>实现点击按钮跳转:</p>    <p>配置URL schemes:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/47e32d97984294ab3c92c6a395c7b36d.png"></p>    <p>在按钮点击的方法里实现:(此处小编用的代理)</p>    <pre>  <code class="language-objectivec">NSString *urlStr = [NSString stringWithFormat:@"medicalWdget://%li",index];    NSURL *url = [NSURL URLWithString:urlStr];    [self.extensionContext openURL:url completionHandler:^(BOOL success) {  }];</code></pre>    <p>目前就可以跳转到app了!当然是四个按钮,你需要再AppDelegate里面进行处理:</p>    <pre>  <code class="language-objectivec">-(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 *)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;  }</code></pre>    <p>然后处理相应的通知,判断url.host点击的第几个按钮!</p>    <p>适配iOS 10:</p>    <p>NCWidgetDisplayMode</p>    <pre>  <code class="language-objectivec">NCWidgetDisplayModeCompact, // Fixed height  NCWidgetDisplayModeExpanded, // Variable height</code></pre>    <p>需要再设置高度之前设置该属性;</p>    <pre>  <code class="language-objectivec">-(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);  }</code></pre>    <p>模式改变后会执行下面这个方法:</p>    <pre>  <code class="language-objectivec">-(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}    }  }</code></pre>    <p>两种设计风格:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7226464f7120ff86ad7bbfa2f4d4f3d5.png"></p>    <p style="text-align:center">iPhone 7的界面效果</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c75dd62d4569d60041c18ae36d9ceec5.png"></p>    <p style="text-align:center">iPhone 6(iOS8.4)的界面效果</p>    <p><strong>扩展:</strong></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ba5aae11e2760501c068af155e09d2b8.png"></p>    <p>注意:有人看到这种界面可能会想到tableview实现,开始小编也是这样想的。可是实现起来发现布局都是乱的(具体原因没有找出来,如哪位研究出来了,麻烦交流一下)!所以建议大家使用UIView就可以了!</p>    <p>网络请求数据:</p>    <pre>  <code class="language-objectivec">@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 {        }</code></pre>    <p>如果是http请求需要配置info.plist文件:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0bd75d28edbfb45e490fd9e24added5b.png"></p>    <p>注意:从2017年1月起所有请求需要时https 的,否则审核就会被拒!web连接可以是http的,但是也需要配置!</p>    <p><strong>APP数据互通</strong>:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/43008f707fa1b7ec2d7af8bc1deb813c.png"></p>    <p>通过一下沙盒存储方法存储数据和获取数据!</p>    <p>与app使用相同的方法文件:</p>    <p>将公共的文件打包成framework之后,进行相关的配置;</p>    <p>调试:</p>    <p>选择不同的项目运行,在相应的项目里的断点就会起作用!</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ff5a747a19ef73b1a55057c9e831f113.png"></p>    <p>补充:widget的上线也是需要单独申请APP ID的 需要配置证书和Provisioning Profiles文件!</p>    <p>?没有配置相关证书时:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9de2992851c0daa2789377d5c2e98cc6.png"></p>    <p>配置证书及描述文件:(列举一些)</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3760a0e3156ad1e425a6cdcd07f85960.png"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/10a408e600b88054b7f5833988c130d5.png"></p>    <p>证书与描述文件配置好之后:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c5c4751e1d87ec54632369a805fc2095.png"></p>    <p> </p>    <p>来自:http://www.cocoachina.com/ios/20161115/18078.html</p>    <p> </p>