起因:项目需要,因为发现竞品公司已经有3D touch 的应用,于是写了这么一套东西。
组件化一直在做,挑了一个最近刚刚发布的热乎的组件 3DTouch 组件
不上代码都是耍流氓 上链接 ---> LG3DTouch
集成方式
pod 'LG3DTouch'
组件是怎么弄出来怎么发布的这篇就不细说了
看货
@interface LG3DTouchManager : NSObject
+ (instancetype)shareManager;
#pragma mark - 图标事件
/**
注册元素 自动完成覆盖 使其一定能被注册
@param shortcutItem 元素
*/
- (void)autoRegisterUnit:(LGShortcutItem *)shortcutItem;
/**
移除所有item元素
*/
- (void)reset;
/**
删除最近的一个元素
*/
- (void)removeNearlyItem;
#pragma mark - 预览事件
/**
注册预览事件
@param target 预览需要的模型
*/
- (void)registerPreviewController:(LG3DTouchProxyTarget *)target
paramBlock:(void (^)(UIViewController *previewController,UIView *sourceView))block;
/**
注册预览状态下的按钮
@param title <#title description#>
@param style <#style description#>
@param handler <#handler description#>
@return <#return value description#>
*/
- (UIPreviewAction *)registerActionWithTitle:(NSString *)title style:(UIPreviewActionStyle)style handler:(void(^)(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController))handler;
@end
初始化是普通单例 后续改进的话可能避免使用单例,或避免单例持久化,毕竟我们在使用app的过程中,很少用到3D Touch 可以说几乎不用。没必要将这个单例持久化。
一共有这么几种调用体
一、app icon的按压事件
按压弹出的是一个列表,列表就需要元素。
我这里用这种方式添加元素
- (void)autoRegisterUnit:(LGShortcutItem *)shortcutItem;
目的是为了增强封装性,就别找系统的UIApplicationShortcutItem
了,费劲。
在LGShortcutItem
中封装了url
这个属性
@interface LGShortcutItem : NSObject
@property (nonatomic, strong) NSString *typeName;
@property (nonatomic, strong) NSString *localizedTitle;
@property (nonatomic, strong) NSString *localizedSubtitle;
@property (nonatomic, strong) UIApplicationShortcutIcon *icon;
/**
遵循跳转协议
*/
@property (nonatomic, strong) NSString *url;
这个url
属性在UIApplicationShortcutItem
的userInfo
属性里,直接用糖衣语法,获取方式为userInfo[@"url"]
元素的初始化方法也准备了
- (instancetype)initWithIconType:(UIApplicationShortcutIconType)iconType
typeName:(NSString *)typeName
localizedTitle:(nonnull NSString *)localizedTitle
localizedSubtitle:(nullable NSString *)localizedSubtitle
jumpUrl:(NSString *)url;
- (instancetype)initWithIconName:(NSString *)iconName
typeName:(NSString *)typeName
localizedTitle:(nonnull NSString *)localizedTitle
localizedSubtitle:(nullable NSString *)localizedSubtitle
jumpUrl:(NSString *)url;
就这样,通过
- (void)autoRegisterUnit:(LGShortcutItem *)shortcutItem;
成功注册了一个图标。
核心代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
LGShortcutItem *item1 = [[LGShortcutItem alloc] initWithIconType:UIApplicationShortcutIconTypeHome typeName:@"GoType1" localizedTitle:@"去目的地1" localizedSubtitle:nil jumpUrl:@"action://page=home"];
LGShortcutItem *item2 = [[LGShortcutItem alloc] initWithIconType:UIApplicationShortcutIconTypeTask typeName:@"GoType2" localizedTitle:@"去目的地2" localizedSubtitle:@"子标题2" jumpUrl:nil];
[[LG3DTouchManager shareManager] autoRegisterUnit:item1];
[[LG3DTouchManager shareManager] autoRegisterUnit:item2];
return YES;
}
通过实验发现 iPhone 3D Touch 最多只能展示4个元素。
那么就有这种情况:我在appDelegate
的didFinishLaunchingWithOptions
添加了三个元素,但是四个元素需要通过业务逻辑才能确定,比如 某滴打车,你需要填写了家和公司两个地址才会在按压图标的时候有另外两个元素添加进去。
所以在代码实现上,添加了一个操作
- (void)autoRegisterUnit:(LGShortcutItem *)shortcutItem {
[self unlock];
[self registerUnit:shortcutItem];
if (self.shortcutItems.count > 4) {
[self removeNearlyItem];//在已经大于4个的情况下添加元素失败,删除最上层的元素,再注册新元素到最上层,添加成功
[self autoRegisterUnit:shortcutItem];
}
[self lock];
}
很好奇 lock 和 unlock内是什么蜜汁操作?
我的想法很简单,就是将本地的元素列表和UIApplication
内存储同步,上代码
- (void)lock {
#if DEBUG
NSAssert(self.shortcutItems.count, @"self.shortcutItems 不可用为空数组");
NSAssert(self.shortcutItems.count <= 4, @"LG3DTouchManager 最多允许添加4个元素 可以使用autoRegisterUnit:方法来自动覆盖");
#endif
//只有调用了 lock 方法 3DTouch元素才会生效
[UIApplication sharedApplication].shortcutItems = self.shortcutItems;
_isLock = YES;
}
- (void)unlock {
self.shortcutItems = (NSMutableArray *)[UIApplication sharedApplication].shortcutItems;
_isLock = NO;
}
蜜汁lock·····
本意是打算防止在项目中任意地方随意添加删除元素,写着写着觉得还是组件内写好算了。
元素点击的回调
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
//只要是点击item 进来的,就会走这里,不论程序是在后台还是新启动
NSString *url = shortcutItem.userInfo[@"url"];
if(url.length) {
NSLog(url);//输出跳转地址 这里url 可以根据自定义的跳转协议来跳到指定页面。内容可以参考http的get请求方式传递参数
}
}
二、tableViewCell 的按压事件
其实整个流程都比较简单 在cellForRowAtIndexPath
内调用
直接上代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *str = [NSString stringWithFormat:@"%@",[indexPath.row]];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reusedId];
cell.contentView.backgroundColor = [UIColor whiteColor];
cell.textLabel.text = str;
//注册预览页和当前页
LG3DTouchProxyTarget *target = [[LG3DTouchProxyTarget alloc] initWithPreview:@"LGPresentationViewController" current:self sourceView:cell];
[[LG3DTouchManager shareManager] registerPreviewController:target paramBlock:^(UIViewController *previewController,UIView *sourceView) {
//在这里传递参数
LGPresentationViewController *vc = (LGPresentationViewController *)previewController;
UITableViewCell *returnCell = (UITableViewCell *)sourceView;
vc.strInfo = returnCell.textLabel.text;
}];
return cell;
}
这里需要用 LG3DTouchProxyTarget
包装一下
这个操作是不是有点熟悉,和消息转发机制里的
NSInvocation
重新包装方法的样子差不多
这个方式还可以用于NSTimer 里的解除强引用。
目的是为了让单例不直接持有self,这样就可以避免强引用。
然后呢?
没有了。
是不是以为要接代理之类的,并没有。一切乱七八糟的注册和判断和预览界面的初始化都在组件里完成了,是不是很方便。
只要实现了这个方法,预览页,当前页,当前控件就 高聚和低耦合的方式连接在了一起。
注:为了避免重复创建预览页的实例,只需要传递预览页的类名字就可以了。NSString
形式,划重点。
- (id)initWithPreview:(NSString *)previewControllerName current:(UIViewController *)current sourceView:(UIView *)sourceView;
那么有的界面可能需要传递参数才能展示内容,参数怎么传递呢
在这里
[[LG3DTouchManager shareManager] registerPreviewController:target paramBlock:^(UIViewController *previewController,UIView *sourceView) {
//在这里传递参数
LGPresentationViewController *vc = (LGPresentationViewController *)previewController;
UITableViewCell *returnCell = (UITableViewCell *)sourceView;
vc.strInfo = returnCell.textLabel.text;
}];
组件返回了一个预览页的实例,和按压控件的实例。
现在核心功能实现完毕。
附一张第一次提交cocoaPods成功的截图。
网友评论