Widget

作者: 小凡凡520 | 来源:发表于2019-01-24 16:18 被阅读0次

    iOS 8中增加的广受欢迎的功能之一就是能够创建各种类型的扩展(Extensions)。通过使用App Extensions,可以让应用程序与其它应用程序、系统共享功能。

    一、什么是扩展

    扩展是一个二进制文件,其不是完整app,必须包括在containing app中,同时containing app实现部分不能为空。Containing app可以是已有app,也可以是新创建的app。虽然扩展不能单独分发,但其与containing app有各自的沙盒。

    Host app启动和控制扩展程序。例如,如果创建的是share extension,host app可能是Safari浏览器;如果创建的是widget extension,host app可能是系统的Today app。系统上每一个支持添加扩展的位置称为扩展点(extension point)。

    为了创建扩展,需要为containing app添加target。Xcode提供的扩展模板包含了适用于每个extension point的框架,方便扩展与host app交互。

    3151492-d0249e91842e4465.png
    二、Widget扩展

    Widget(也称为Today Extension)是其中一种类型的扩展(extension),可以在通知中心、锁屏界面显示少量、及时的信息,或提供特定功能。例如:新闻app的小组件显示头条新闻;日历app提供了两个小组件,一个widget显示今天的事件,一个widget显示接下来的事件;备忘录的widget显示最近的笔记、快速创建笔记等功能。Widgets可以高度自定义,可以包括按钮、文本、图片等,但不支持输入文字。

    在iOS中,由于widget不允许使用键盘输入,因此用户需要containing app来配置widget内容和行为。例如,在Stocks widget中,用户可以在股价不同表示方式间切换,但必须打开Stocks应用程序来管理股票列表

    三、添加target

    打开Xcode,使用Simple View Application模板创建名称为Widget Swift的应用,点击File > New > Target添加target。Target类型选择Today Extension,名称为UsedSpaceWidget。Xcode会创建一个scheme,并请求激活该scheme,点击Activate激活该scheme。

    Today Extension模板为主类(principal class)TodayViewController提供实现文件、Info.plist文件、storyboard文件。

    屏幕快照 2019-01-24 下午3.39.00.png

    在iOS中,如果你不想使用模版提供的storyboard,移除NSExtensionMainStoryboardkey和对应value。添加NSExtensionPrincipalClass键,并设置对应视图控制器为键值。

    四、创建用户界面

    由于Today视图空间有限、需要快速响应,因此,不要创建太大的widget。在iOS、macOS平台上,不能修改widget宽度,只能修改高度。

    五、显示数据

    为保持widget显示最新内容,系统会定期捕获widget视图快照。当widget呈现时将显示最新snapshot,直到系统使用widget实时版本替换该快照

    六、更新界面

    因为widget是一个视图控制器,可以在viewDidLoad方法中更新界面,在TodayViewController.swift文件中添加updateUI()方法更新UI,并在viewDidLoad中调用

    Widget的生命周期非常短,几乎每次进入通知中心,TodayViewController的viewDidLoad方法都会被调用。随后,系统会在适当的时刻调用widgetPerformUpdate(completionHandler:)方法执行更新,在没有内容更新时,应当返回noData。

    七、NCWidgetDisplayMode

    在widget中,有以下两种模式用来显示数据,compact:Widget高度为110,expanded:根据内容显示widget高度。
    当设置为compact时,app将只支持compact mode,不支持展开和折叠功能。widget高度固定为110。
    当设置为expanded时,app将同时支持compact mode和expanded mode,展开和折叠功能也将可用。

    根据display mode调整widget高度

    // 监听展开或折叠
    func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
        if activeDisplayMode == .compact {
            print("compact")
        } else {
            print("expanded")
        }
    }
    
    八、打开containing app

    Widget应当能够独立运行。但是,如果用户需要做一些widget没有提供的功能,应当在用户点击对应内容时跳转到containing app,不要为此添加额外的按钮,占用有限可用空间。例如,在日历小部件中,可用点击事件以在日历app中将其打开。切勿使用小部件打开其它应用。

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view from its nib.
        
        self.extensionContext?.widgetLargestAvailableDisplayMode = .expanded
        
        let url = URL(string: "widget://first/word?From%20Widget%20To%20First%20VC")!
        extensionContext?.open(url, completionHandler: { (ret) in
            print("s")
        })
    }
    
    3151492-8c0cccb83b89d756.png

    当扩展打开containing app时,在AppDelegate的application(_ : url: sourceApplication: annotation: )方法中处理后续操作

    - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
        NSLog(@"ss");
        return true;
    }
    
    九、通过App Groups共享数据

    尽管扩展程序bundle嵌套在其containing app bundle中,但其处于各自沙盒中,不能直接访问彼此容器,也就无法直接在扩展程序和containing app间共享数据。如需共享数据,需要使用App Groups。

    十、从containing app中显示、隐藏Widget

    Widget和containing app都可以使用NCWidgetController类对象操作widget是否有内容需要显示。例如,机票类应用在没有行程时隐藏widget。因为NCWidgetController类可以协调widget和containing app决定小部件是否有内容需要显示,所以在widget和containing app间没有通信时将不需要使用NCWidgetController类。

    一般,widget有内容时显示在Today视图。如果widget没有要显示的内容,可以通过调用NCWidgetController类的setHasContent(:forWidgetWithBundleIdentifier:)方法隐藏小部件。如果containing app稍后检测到有内容需要显示,containing app可以通过调用setHasContent(:forWidgetWithBundleIdentifier:)方法显示widget,即使此时widget没有运行。

    @IBAction func handleButtonTapped(_ sender: UIButton) {
        if sender.titleLabel?.text == "Show Widget" {
            NCWidgetController().setHasContent(true, forWidgetWithBundleIdentifier: "pro648.Widget-Swift.UsedSpaceWidget")
            sender.setTitle("Hide Widget", for: .normal)
        } else {
            NCWidgetController().setHasContent(false, forWidgetWithBundleIdentifier: "pro648.Widget-Swift.UsedSpaceWidget")
            sender.setTitle("Show Widget", for: .normal)
        }
    }
    

    相关文章

      网友评论

          本文标题:Widget

          本文链接:https://www.haomeiwen.com/subject/lizggftx.html