Widget的簡單應用並適配iOS10

上天眷顧我發表於2016-10-24

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檔案!

沒有配置相關證書時:



配置證書及描述檔案:(列舉一些)




證書與描述檔案配置好之後:


感謝您的閱讀,希望對您有所幫助!



文/芝麻綠豆(簡書作者)
原文連結:http://www.jianshu.com/p/42516ee26a45
著作權歸作者所有,轉載請聯絡作者獲得授權,並標註“簡書作者”。

相關文章