前言: 参考了不少文章,其都是使用KVO监听状态变化实现的。但是这样需要多每个tableView对象进行监听处理,或者新建一个父类去管理这些(适合新项目)。对于老旧项目来说比较麻烦, 所以本文诞生了。
思路
核心思路同样是通过监听属性状态变化,但不是使用KVO,而是通过分类
特点
- 只需要引入该分类接口,不需要对原有项目进行任何代码更改
- 耦合度低,因为不需要改动原代码,所以随时可以从项目中剥离
- 可以通过属性,控制页面振动反馈效果的开启关闭:需要导入头文件
- 适合新老项目
代码演示
#import "MJRefreshComponent.h"
NS_ASSUME_NONNULL_BEGIN
@interface MJRefreshComponent (ImpactFeedbackGenerator)
// MARK: - 添加阀门控制属性
/// 下拉刷新 是否需要禁止振动反馈, 默认NO
@property(nonatomic, assign) BOOL disableFeedbackOfMJHeader;
/// 上拉加载更多 是否需要禁止振动反馈, 默认NO
@property(nonatomic, assign) BOOL disableFeedbackOfMJFooter;
// MARK: - 发起振动api
/// 发起振动
- (void)tl_impactOccurred;
+ (void)tl_impactOccurred;
@end
NS_ASSUME_NONNULL_END
#import "MJRefreshComponent+ImpactFeedbackGenerator.h"
#import <AudioToolbox/AudioToolbox.h>
#import <sys/utsname.h>
@implementation MJRefreshComponent (ImpactFeedbackGenerator)
// MARK: - 方法交换、监听状态改变
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = NSClassFromString(@"MJRefreshComponent");
Method method1 = class_getInstanceMethod(cls, @selector(setState:));
Method method2 = class_getInstanceMethod(cls, @selector(tl_setState:));
method_exchangeImplementations(method1, method2);
});
}
- (void)tl_setState:(MJRefreshState)state {
if (self.state == MJRefreshStateIdle && state == MJRefreshStatePulling) {
BOOL flag = YES;
if ([self isKindOfClass:NSClassFromString(@"MJRefreshHeader")] && self.disableFeedbackOfMJHeader) {
flag = NO;
}
if ([self isKindOfClass:NSClassFromString(@"MJRefreshBackFooter")] && self.disableFeedbackOfMJFooter) {
flag = NO;
}
if (flag) {
[self tl_impactOccurred];
}
}
[self tl_setState:state];
}
// MARK: - 属性关联
- (void)setDisableFeedbackOfMJHeader:(BOOL)disableFeedbackOfMJHeader {
objc_setAssociatedObject(self, @selector(disableFeedbackOfMJHeader), @(disableFeedbackOfMJHeader), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)disableFeedbackOfMJHeader {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setDisableFeedbackOfMJFooter:(BOOL)disableFeedbackOfMJFooter {
objc_setAssociatedObject(self, @selector(disableFeedbackOfMJFooter), @(disableFeedbackOfMJFooter), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)disableFeedbackOfMJFooter {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
// MARK: - 发起震动
- (void)tl_impactOccurred {
[MJRefreshComponent tl_impactOccurred];
}
+ (void)tl_impactOccurred {
if (@available(iOS 10.0, *)) {
struct utsname systemInfo;
uname(&systemInfo);
NSString *platform = [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
if ([platform containsString:@"iPhone"]) {
NSString *p = [platform componentsSeparatedByString:@","].firstObject;
p = [p stringByReplacingOccurrencesOfString:@"iPhone" withString:@""];
if (p.intValue > 8) { // iPhone 7 +
UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
[generator prepare];
[generator impactOccurred];
return;
}
}
}
// iPhone 7之前的手机或iOS10之前的版本
// AudioServicesPlayAlertSound(kSystemSoundID_Vibrate); // 效果差,力度和时间太过
AudioServicesPlayAlertSound(1520); // 1520与1519私有API
}
@end