转载

如何用纯代码构建一个 Widget(2014)

前言

随着iOS8的发布 各种iPhone的新玩法出现了 其中最引人关注的就是 today extension (也叫做widget) 这个在android上存在了多年的小玩意 也是iPhone一直被人诟病的东西 终于能用上了

网上有很多相关的文章教你如何编写一个简单的widget 但是却没有一篇适合我们这种纯代码的拥趸(也有很多人说应该放弃纯代码 改用Storyboard了) 那么接下来我就说说 如何用纯代码的方式来构建一个widget

准备

首先 你得有个正常的app项目(这是必须的 extension必须依附于某个app中 当然 不这样 你也无法单独安装某个widget)

打开项目工程 选择新建一个target 在 Application Extension 中选择 today exntension 然后填入名字 确认即可

如何用纯代码构建一个 Widget(2014)

如何用纯代码构建一个 Widget(2014)

这时你的项目里多个一个target 同时也多了下面

如何用纯代码构建一个 Widget(2014)

接下来 删掉这个讨厌的 MainInterface.storyboard 然后修改plist文件中的 NSExtension 字段

  • 删掉 NSExtensionMainStoryboard 字段
  • 添加 NSExtensionPrincipalClass 字段 并设为 TodayViewController (你也可以指定其他的ViewController)

如何用纯代码构建一个 Widget(2014)

修改完以后 Widget就可以开始编译运行了

运行

关于调试Widget 我推荐使用模拟器而不用真机 因为在研究过程中我发现真机调试的效果非常差 经常提示无法连接到手机(也有可能是5S的性能够不?) 导致无法正常的debug 或者无法reinstall 而模拟器则好点(至少能顺利的打印出log) 但是每次修改好代码以后 最好都先退出模拟器 再重新编译运行 如果你退出重新运行时提示下面这个错误 不用怕 多运行两次就ok了

如何用纯代码构建一个 Widget(2014)

Command+R 编译运行 会弹出提示框让你选择宿主app 就选择默认的today就好了

如何用纯代码构建一个 Widget(2014)

如无意外 模拟器启动时会自动打开today 并显示你的widget

如何用纯代码构建一个 Widget(2014)

是不是发现什么都没有? 如果用Storyboard构建的Widget 会默认有个 Hello 可我们弄出来的Widget 却啥都没有 连高度都没有

那么问题来了…

修改

首先 我们设置一下widget的高度 并添加一个contentView和一个button(注意 这里我使用的是 Masonry 来完成autolayout 相关信息可见我的上一篇文章: Masonry介绍与使用实践(快速上手Autolayout) )


@interface TodayViewController () <NCWidgetProviding>

@property (strong, nonatomic) UIView *contentView;
@property (nonatomic, strong) UIButton *btnTest;

@end


- (void)viewDidLoad {
[super viewDidLoad];

//使用preferredContentSize设置大小 且只用设置高度就好了
self.preferredContentSize = CGSizeMake(0, 200);

__weak __typeof(&*self)ws = self;

self.contentView = [UIView new];
self.contentView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.contentView];

[self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(ws.view);
}];

self.btnTest = [UIButton buttonWithType:UIButtonTypeCustom];
[self.btnTest setTitle:[[NSDate date] description] forState:UIControlStateNormal];
self.btnTest.backgroundColor = [UIColor redColor];
[self.btnTest addTarget:self action:@selector(actionTest) forControlEvents:UIControlEventTouchUpInside];

[self.contentView addSubview:self.btnTest];

[self.btnTest mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(ws.contentView);
make.size.mas_equalTo(CGSizeMake(300, 40));
}];
}

运行一下 看看效果

如何用纯代码构建一个 Widget(2014)

控件是出来了 可是没有如我们的意 旁边还空了一块 原来widget默认会有一个inset 那么如何取消这个inset呢? 重载如下方法即可

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

修改完以后您再看

如何用纯代码构建一个 Widget(2014)

一个Widget的模子就这样构建完成了

进阶

接下来 我们给button加个点击事件 用来改变widget的大小

- (void) actionTest
{
self.preferredContentSize = CGSizeMake(0, self.contentView.frame.size.height>250?200:300);
}

如何用纯代码构建一个 Widget(2014)

测试发现效果”还可以” 为什么仅仅是”还可以”呢 可以看到当size变化时 其他区域其实是有个动态变化的效果 但是我们的widget的变化是立即的 所以看上去不那么流畅(storyboard里就不存在这个问题了 因为使用了autolayout)

那么我们可不可以也使用autolayout 而不设置这个 preferredContentSize 呢? 答案是可以的

首先修改viewDidLoad的代码

- (void)viewDidLoad {
[super viewDidLoad];

//去掉这一步
//self.preferredContentSize = CGSizeMake(0, 200);

__weak __typeof(&*self)ws = self;

self.contentView = [UIView new];
self.contentView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.contentView];

[self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(ws.view);
//设置内部view的高度(一定要设置高优先级 不然会有冲突)
make.height.mas_equalTo(@200).priorityHigh();
}];

...
...
...

}

然后修改按钮的动作

- (void) actionTest
{
//去掉这一步
//self.preferredContentSize = CGSizeMake(0, self.contentView.frame.size.height>250?200:300);

//更新autolayout
[self.contentView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(@(self.contentView.frame.size.height>250?200:300)).priorityHigh();
}];
}

试着运行一下 你会发现世界变得很美丽了

如何用纯代码构建一个 Widget(2014)

至此 发挥你的想象吧 you can do whatever you want!

小结

所有的准备工作都已经做完了 编写一个Widget也变得很简单 你可以像写任何一个ViewController一样来写Widget 而纯代码的方式我相信会让很多人更得心应手

正文到此结束
Loading...