Commit 42658103 authored by 乔金柱's avatar 乔金柱

Merge branch 'ajp/7.27.0_expo' into 'master'

add Exposure

See merge request !54
parents b2404a0e ed064ed2
PODS:
- GMCache (1.0.1):
- TMCache (= 2.1.0)
- GMPhobos (2.0.3):
- GMPhobos (2.0.4):
- GMCache
- MagicalRecord
- MJExtension
......@@ -28,7 +28,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
GMCache: b78d8e46db864405e91d226ce640cc80d966c611
GMPhobos: 6bd14d7b98841b6a3e35c5aa8b1f091ad07bbbf7
GMPhobos: fd6f7e453e71fb7f51d8c5601e605c110ddfda92
MagicalRecord: 53bed74b4323b930992a725be713e53b37d19755
MJExtension: 635f2c663dcb1bf76fa4b715b2570a5710aec545
TMCache: 95ebcc9b3c7e90fb5fd8fc3036cba3aa781c9bed
......
......@@ -23,7 +23,7 @@ Pod::Spec.new do |s|
s.source = { :git => "git@git.wanmeizhensuo.com:gengmeiios/GMPhobos.git", :tag => s.version.to_s }
s.platform = :ios, '8.0'
s.source_files = 'GMPhobos/Classes/*.{h,m}'
s.source_files = 'GMPhobos/Classes/**/*'
s.dependency 'GMCache'
s.dependency 'MJExtension'
s.dependency 'MagicalRecord'
......
//
// GMExposureManager.h
// Gengmei
//
// Created by Mikasa on 2019/6/21.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
#import "UIView+Tracker.h"
#import "UIScrollView+Tracker.h"
#import "UIViewController+Tracker.h"
#import "NSObject+Tracker.h"
/**
* 精准曝光
* 开始曝光时机:1、页面出现 2、退到后台
*/
typedef NS_ENUM(NSInteger, GMExpoUploadMode) {
GMExpoUploadModeImmediate = 0,// 立即上报
GMExpoUploadModePage = 1,// 按页面上报
GMExpoUploadModeApp = 2 // 按App上报
};// 精准曝光数据上报模式
typedef NS_ENUM(NSInteger, GMViewTrackerType) {
GMViewTrackerTypeUIViewSetHidden = 0,
GMViewTrackerTypeUIViewSetAlpha = 1,
GMViewTrackerTypeUIViewDidMoveToWindow = 2,
GMViewTrackerTypeUIScrollContentOffset = 3,
GMViewTrackerTypeUIScrollDragging = 4,
GMViewTrackerTypeUIViewSetExposure = 5,
GMViewTrackerTypeUIViewEnterForeground = 6,
GMViewTrackerTypeUIViewDidAppear = 7,
GMViewTrackerTypeUIViewDidDisappear = 8,
GMViewTrackerTypeUIViewWillResignActive = 9
}; // 记录方式
@interface GMExposureManager : NSObject
/// 设置上报数据地址
@property (nonatomic, copy) NSString *uploadExposureAPI;
/**
* 精准曝光数据上报模式
* 默认为 GMExpoUploadModePage
*/
@property (nonatomic, assign) GMExpoUploadMode uploadMode;
/** view出现的屏幕的占比 默认0.01f */
@property (nonatomic, assign) CGFloat exposureDimThreshold;// view visble in size > exposureDimThreshold
/** 曝光最少时长 默认0.01s (目前暂未使用)*/
@property (nonatomic, assign) NSTimeInterval exposureMinDuration;// view duration > exposureMinDuration
@property (nonatomic, assign) BOOL inBackground;// 当前App是否进入后台
@property (nonatomic, assign) BOOL ignoreTrackModel;// 是否需要忽略滚动模式
/**
GMExposureManager
@return sharedManager
*/
+ (instancetype)sharedManager;
#pragma mark - Deal Tracker
/**
* 结束所有页面的曝光并上报
* 会先将所有页面上正在曝光的View置为end,再进行数据上传
*/
- (void)endExpoTrcakerForAllPage;
/**
* 结束指定页面的曝光并上报
* 会先将指定页面上正在曝光的View置为end,再进行数据上传
* 多用于下拉刷新前需要曝光当前的vc的数据
@param pageCtrl 指定页面
*/
- (void)endExpoTrcakerForPageCtrl:(UIViewController *)pageCtrl;
#pragma mark - Deal Data
/**
上传指定页面的曝光数据
@param pageName 指定页面
*/
- (void)uploadExposureDataForPage:(UIViewController *)pageCtrl;
/**
上传所有页面的曝光数据
*/
- (void)uploadAllExposureData;
// 存储曝光数据
- (void)startStoreExposureData;
/**
清理指定页面的曝光数据
@param pageName 指定页面
*/
- (void)clearExposureDataForPage:(UIViewController *)pageCtrl;
// 清理当前数据
- (void)clearAllExposureData;
/**
当前View上是否有需要精准曝光的数据
@param view View
@return yes/no
*/
+ (BOOL)haveExposureForView:(UIView *)view;
#pragma mark - View Visible/UnVisibel
/**
获取当前View的可见状态
@param view view
@param 可见状态
*/
+ (BOOL)isViewVisible:(UIView *)view;
/**
设置当前view是否可见状态
@param view view
@param recursive 是否递归子view
@param ignoreTracking 是否忽略手势滚动时追踪
*/
+ (void)fetchViewForVisibleState:(UIView *)view trackerType:(GMViewTrackerType)trackerType recursive:(BOOL)recursive;
/** 设置view的曝光状态
* view 可见 && 状态none或者end——> 开始曝光
* view 可见 && 状态start——> 正在曝光,不做处理
* view 不可见 && 状态start ——> 结束曝光
* view 不可见 && 状态none或者end ——> 不做处理
*/
- (void)exposureStatusForView:(UIView *)view inPageCtrl:(UIViewController *)inPageCtrl;
@end
This diff is collapsed.
//
// GMExposureModel.h
// Gengmei
//
// Created by Mikasa on 2019/6/25.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
// 基于页面的精准曝光Item
@interface GMExposureItemModel : NSObject
/** 记录上滑总次数 */
@property (nonatomic, assign) NSInteger up_slide_times;
/** 记录下滑总次数 */
@property (nonatomic, assign) NSInteger down_slide_times;
/** 记录向上加载总次数 */
@property (nonatomic, assign) NSInteger up_loading_times;
/** 记录向下加载总次数 */
@property (nonatomic, assign) NSInteger down_loading_times;
/** 当前tab_name */
@property (nonatomic, copy) NSString *tab_name;
/** 当前page_name */
@property (nonatomic, copy) NSString *page_name;
/** 当前business_id */
@property (nonatomic, copy) NSString *business_id;
/** 当前referrer */
@property (nonatomic, copy) NSString *referrer;
/** 当前referrer */
@property (nonatomic, copy) NSString *referrerId;
/** 当前filter_f */
@property (nonatomic, copy) NSString *filter_f;
/** 曝光卡片数据 */
@property (nonatomic, strong) NSMutableArray *exposure_cards;
/** 是否精准曝光 */
@property (nonatomic, copy) NSString *is_exposure;
@end
//
// GMExposureModel.m
// Gengmei
//
// Created by Mikasa on 2019/6/25.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
#import "GMExposureModel.h"
@implementation GMExposureItemModel
- (instancetype)init {
if (self = [super init]) {
_exposure_cards = [NSMutableArray array];
_is_exposure = @"1";
}
return self;
}
@end
//
// GMExposureProtocol.h
// Gengmei
//
// Created by Mikasa on 2019/6/21.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
typedef NS_ENUM(NSInteger, GMViewExpoStatus) {
GMViewExpoStatusNone = 0,// 无状态
GMViewExpoStatusStart = 1,// 开始曝光
GMViewExpoStatusIng = 2,// 正在曝光(暂未使用,暂时先保留)
GMViewExpoStatusEnd = 3 // 结束曝光
};
@protocol GMViewExposureProtocol <NSObject>
@property (nonatomic, weak) UIViewController *pageCtrl;// 当前viewz所在页面
/** view别名 */
@property (nonatomic, copy) NSString *viewName;
/** view别名 */
@property (nonatomic, copy) NSString *inTime;
/** view别名 */
@property (nonatomic, copy) NSString *outTime;
/** 绝对位置 */
@property (nonatomic, copy) NSString *absolute_position;
/** 相对位置(相对屏幕所在位置)*/
@property (nonatomic, copy) NSString *relative_position;
/** 额外参数*/
@property (nonatomic, strong) NSDictionary *extra_param;
/** 曝光状态 */
@property (nonatomic, assign) GMViewExpoStatus expoStatus;
@end
@protocol GMPageExposureProtocol <NSObject>
/**
* 是否获取到数据立即发送,YES的话不会进行去重逻辑,需要数据去重
*/
@property (nonatomic, assign) BOOL needImmediatelySend;
@property (nonatomic, assign) BOOL needExpo;// 页面需要精准曝光
/** 记录上滑总次数 */
@property (nonatomic, assign) NSInteger up_slide_times;
/** 记录下滑总次数 */
@property (nonatomic, assign) NSInteger down_slide_times;
/** 记录向上加载总次数 */
@property (nonatomic, assign) NSInteger up_loading_times;
/** 记录向下加载总次数 */
@property (nonatomic, assign) NSInteger down_loading_times;
/** 额外参数 */
@property (nonatomic, strong) NSDictionary *extra_param;
@end
@protocol GMExposureProtocol <NSObject>
/** 精准曝光数据 */
@property (nonatomic, strong) NSDictionary *exposure;
@end
//
// GMTrackerProtocol.h
// Gengmei
//
// Created by MoMo on 2019/8/15.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
//#import "NSString+Tools.h"
#ifndef GMTrackerProtocol_h
#define GMTrackerProtocol_h
#define kCpcReferer @"cpc_referer"
#define kIsCpc @"is_cpc"
// 页面CPC
typedef NS_ENUM(NSInteger, GMCpcReferer) {
GMCpcNone = 9999 // None
};
@protocol GMTrackerProtocol <NSObject>
//
///** 页面名称 */
//@property (nonatomic, copy) NSString *page_name;
///** 页面业务ID */
//@property (nonatomic, copy) NSString *business_id;
///** 当前页面tab名称 */
//@property (nonatomic, copy) NSString *tab_name;
///** 上一个页面的tab名称 */
//@property (nonatomic, copy) NSString *referrer_tab_name;
///** 上一个页面的名称 */
//@property (nonatomic, copy) NSString *referrer;
///** 上一个页面的业务ID */
//@property (nonatomic, copy) NSString *referrer_id;
@end
@protocol GMCpcProtocol <NSObject>
/** cpc扣费 */
@property (nonatomic, assign) GMCpcReferer cpc_referer;
/** 是否cpc卡片 */
@property (nonatomic, assign) BOOL is_cpc;
@end
#endif /* GMTrackerProtocol_h */
//
// GMHookTool.h
// Gengmei
//
// Created by Mikasa on 2019/6/19.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
#define MethodSwizzlerReplacement(returntype, selftype, ...) ^ returntype (__unsafe_unretained selftype self, ##__VA_ARGS__)
#define MethodSwizzlerReplacementProviderBlock ^ id (IMP original, __unsafe_unretained Class swizzledClass, SEL _cmd)
#define MethodSwizzlerOriginalImplementation(functype, ...) do{\
if (original)\
((functype)original)(self, _cmd, ##__VA_ARGS__);\
}while(0);
typedef id (^MethodSwizzlerProvider)(IMP original, __unsafe_unretained Class swizzledClass, SEL selector);
OBJC_EXTERN BOOL deswizzleAll(void);
@interface NSObject (SwizzleMethod)
/**
swizzleClassMethod
@param selector swizzleClass selector
@param replacementProvider replacementProvider
*/
+ (void)swizzleClassMethod:(SEL)selector withReplacement:(MethodSwizzlerProvider)replacementProvider;
/**
swizzleInstanceMethod
@param selector swizzleInstanceMethod selector
@param replacementProvider replacementProvider
*/
+ (void)swizzleInstanceMethod:(SEL)selector withReplacement:(MethodSwizzlerProvider)replacementProvider;
/**
Swizzle the specified instance method with another selector
@param selector Selector of the method to swizzle.
@param swizzledSelector Selector of the new method will be swizzled.
*/
+ (void)swizzleInstanceMethod:(SEL)selector withSelector:(SEL)swizzledSelector;
/**
Swizzle the specified class method with another selector
@param selector Selector of the method to swizzle.
@param swizzledSelector Selector of the new method will be swizzled.
*/
+ (void)swizzleClassMethod:(SEL)selector withSelector:(SEL)swizzledSelector;
/**
Swizzle the specified instance method with another swizzledClass swizzledMethod
@param originalClass originalClass
@param originalSel originalSel
@param swizzledClass swizzledClass
@param swizzledSelector swizzledSelector
@param noneSel noneSel
*/
+ (void)swizzelInstanceMethodForClass:(Class)originalClass originalSel:(SEL)originalSel swizzledClass:(Class)swizzledClass swizzledSelector:(SEL)swizzledSelector noneSel:(SEL)noneSel;
@end
@interface NSObject (DeSwizzleMethod)
/**
Restore the specified class method by removing all swizzles.
@param selector Selector of the swizzled method.
@return \c YES if the method was successfully restored, \c NO if the method has never been swizzled.
*/
+ (BOOL)deswizzleClassMethod:(SEL)selector;
/**
Restore the specified class method by removing all swizzles.
@param selector Selector of the swizzled method.
@return \c YES if the method was successfully restored, \c NO if the method has never been swizzled.
*/
+ (BOOL)deswizzleInstanceMethod:(SEL)selector;
/**
Restore all swizzled class methods.
@return \c YES if the method was successfully restored, \c NO if no method has never been swizzled
*/
+ (BOOL)deswizzleAllClassMethods;
/**
Restore all swizzled instance methods.
@return \c YES if the method was successfully restored, \c NO if no method has never been swizzled.
*/
+ (BOOL)deswizzleAllInstanceMethods;
/**
Restore all swizzled class and instance methods.
@return \c YES if the method was successfully restored, \c NO if no method has never been swizzled.
*/
+ (BOOL)deswizzleAllMethods;
@end
@interface GMHookTool : NSObject
//判断页面是否实现了某个sel
+ (BOOL)isContainSel:(SEL)sel inClass:(Class)className;
@end
@interface GMWeakObject : NSObject
@property (nonatomic, weak) id obj;
- (instancetype)initWithObj:(id)obj;
@end
This diff is collapsed.
//
// NSObject+Tracker.h
// GMPhobos
//
// Created by Mikasa on 2020/5/9.
//
#import <Foundation/Foundation.h>
#import "GMExposureProtocol.h"
#import "GMTrackerProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (Tracker)<GMCpcProtocol, GMExposureProtocol>
/// 获取通用数据
/// @param responder 当前responder
+ (NSDictionary *)trackerPageParam:(UIResponder *)responder;
/// 进行数据同步,将当前response 上通用数据同步给toResponse
/// @param response 将当前UIResponder 上通用数据同步
/// @param toResponse toResponse
- (void)synchronizePVData:(UIResponder *)response
toResponse:(UIResponder *)toResponse;
@end
NS_ASSUME_NONNULL_END
//
// NSObject+Tracker.m
// GMPhobos
//
// Created by Mikasa on 2020/5/9.
//
#import "NSObject+Tracker.h"
#import "UIResponder+PhobosPV.h"
#import <objc/runtime.h>
@implementation NSObject (Tracker)
/// 获取通用数据
/// @param responder 当前responder
+ (NSDictionary *)trackerPageParam:(UIResponder *)responder {
NSArray *referrerLink = responder.referrerLink;
NSDictionary *params = @{@"page_name" : responder.pageName?:@"",
@"business_id" : responder.businessId?:@"",
@"tab_name" : responder.tabName?:@"",
@"referrer_tab_name" : responder.referrerTabName?:@"",
@"referrer_id" : responder.referrerId?:@"",
@"referrer" : responder.referer?:@""};
NSMutableDictionary *paramsM = [NSMutableDictionary dictionaryWithDictionary:params];
if (referrerLink.count) {
[paramsM addEntriesFromDictionary:@{@"referrer_link" : referrerLink}];
};
return paramsM;
}
/// 进行数据同步
/// 将当前UIResponder 上通用数据同步给 response
/// @param response 需要进行同步的response
- (void)synchronizePVData:(UIResponder *)response toResponse:(UIResponder *)toResponse {
if (!toResponse || !response) {
return;
}
toResponse.pageName = response.pageName?:@"";
toResponse.tabName = response.tabName?:@"";
toResponse.referrerTabName = response.referrerTabName?:@"";
toResponse.businessId = response.businessId?:@"";
toResponse.referer = response.referer?:@"";
toResponse.referrerId = response.referrerId?:@"";
toResponse.referrerLink = response.referrerLink;
}
#pragma mark - Exposure
- (void)setExposure:(NSDictionary *)exposure {
objc_setAssociatedObject(self, @selector(exposure), exposure, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSDictionary *)exposure {
return objc_getAssociatedObject(self, @selector(exposure));
}
@end
//
// UIScrollView+Tracker.h
// Gengmei
//
// Created by Mikasa on 2019/6/19.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
@interface UIScrollView (Tracker)
@property (nonatomic, assign) CGFloat preContentOffsetY;// 记录历史滚动的ContentOffset的Y值
/// 计算当前列表上滑/下拉次数,自动进行+1
- (void)calculateUpOrDownTimes;
@end
@interface UITableView (Tracker)
@end
@interface UICollectionView (Tracker)
@end
//
// UIScrollView+Tracker.m
// Gengmei
//
// Created by Mikasa on 2019/6/19.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
#import "UIScrollView+Tracker.h"
#import "UIViewController+Tracker.h"
#import "NSObject+Swizzle.h"
#import "GMExposureManager.h"
#import <objc/runtime.h>
@implementation UIScrollView (Tracker)
- (void)calculateUpOrDownTimes {
if (self.preContentOffsetY == self.contentOffset.y) {
return;
}
// 判断滚动方向
if (self.preContentOffsetY < self.contentOffset.y) {
self.pageCtrl.up_slide_times++;
} else {
self.pageCtrl.down_slide_times++;
}
}
#pragma mark - Add Method
- (void)setPreContentOffsetY:(CGFloat)preContentOffsetY {
objc_setAssociatedObject(self, @selector(preContentOffsetY), @(preContentOffsetY), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (CGFloat)preContentOffsetY {
return [objc_getAssociatedObject(self, @selector(preContentOffsetY)) floatValue];
}
@end
@implementation UITableView (Tracker)
+ (void)load {
[self swizzleInstanceMethod:@selector(reloadData) withSelector:@selector(swizzle_reloadData)];
}
- (void)swizzle_reloadData {
[self swizzle_reloadData];
if (self.pageCtrl.needExpo) {
__weak typeof(self)weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[GMExposureManager fetchViewForVisibleState:weakSelf
trackerType:GMViewTrackerTypeUIViewSetExposure recursive:YES];
});
}
}
@end
@implementation UICollectionView (Tracker)
+ (void)load {
[self swizzleInstanceMethod:@selector(reloadData) withSelector:@selector(swizzle_reloadData)];
}
- (void)swizzle_reloadData {
[self swizzle_reloadData];
if (self.pageCtrl.needExpo) {
__weak typeof(self)weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[GMExposureManager fetchViewForVisibleState:weakSelf
trackerType:GMViewTrackerTypeUIViewSetExposure recursive:YES];
});
}
}
@end
//
// UIView+Tracker.h
// Gengmei
//
// Created by Mikasa on 2019/6/21.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
#import "GMExposureProtocol.h"
#import "GMTrackerProtocol.h"
@interface UIView (Tracker)<GMCpcProtocol, GMViewExposureProtocol>
@property (nonatomic, assign) BOOL visibleView;// 是否是可见View
#pragma mark - View Method
/**
获取指定View所在的Controller
@param view 指定View
@return 当前所在Controller
*/
+ (UIViewController *)currentControllerForView:(UIView *)view;
/// 判断当前view是否处于屏幕上
/// @param view 指定view
+ (BOOL)viewIsVisible:(UIView *)view;
/**
获取曝光卡片所在的位置
*/
- (void)fechExposurePositionForView;
- (void)synchronizePageTimesToOther:(UIView *)otherView;
@end
//
// UIView+Tracker.m
// Gengmei
//
// Created by Mikasa on 2019/6/21.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
#import "UIView+Tracker.h"
#import "UIViewController+Tracker.h"
#import "NSObject+Swizzle.h"
#import "PhobosUtil.h"
#import "GMExposureModel.h"
#import <objc/runtime.h>
@implementation UIView (Tracker)
#pragma mark - View Method
/**
获取指定View所在的Controller
@param view 指定View
@return 当前所在Controller
*/
+ (UIViewController *)currentControllerForView:(UIView *)view {
UIViewController *currentCtrl;
UIResponder *responder = [view.superview nextResponder];
while (responder && ![responder isKindOfClass:[UIViewController class]]) {
responder = [responder nextResponder];
}
currentCtrl = (UIViewController *)responder;
return currentCtrl;
}
/**
获取曝光卡片所在的位置
*/
- (void)fechExposurePositionForView {
UIView *view = self;
NSInteger absolute_position = [PhobosUtil isNonEmpty:self.absolute_position]?self.absolute_position.integerValue : 0;
UIView *current = (UIView *)view;
UIView *tempView = view;
while ([current isKindOfClass:[UIView class]] && (current != view.pageCtrl.view)) {
if ([current isKindOfClass:[UITableViewCell class]]) {
tempView = current;
}
if ([current isKindOfClass:[UICollectionViewCell class]]) {
tempView = current;
}
if ([current isKindOfClass:[UITableView class]]) {
UITableView *tableView = (UITableView *)current;
UITableViewCell *cell = (UITableViewCell *)tempView;
if ([tableView.visibleCells containsObject:cell]) {
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
if (indexPath) {
// 当有多个分组时,将position转化为绝对路径的position,一维数组
if (tempView == view) {
absolute_position = indexPath.row;
for (NSInteger i = 0; i < indexPath.section; i++) {
absolute_position += [tableView numberOfRowsInSection:i];
}
}
NSInteger relative_position = 0;
if ([[tableView visibleCells] containsObject:cell]) {
relative_position = [[tableView visibleCells] indexOfObject:cell];
view.relative_position = [NSString stringWithFormat:@"%zd",relative_position];
break;
}
}
}
}
if ([current isKindOfClass:[UICollectionView class]]) {
UICollectionView *collectionView = (UICollectionView *)current;
UICollectionViewCell *cell = (UICollectionViewCell *)tempView;
if ([collectionView.visibleCells containsObject:cell]) {
NSIndexPath *indexPath = [collectionView indexPathForCell:cell];
if (indexPath) {
// 当有多个分组时,将position转化为绝对路径的position,一维数组
if (tempView == view) {
absolute_position = indexPath.row;
for (NSInteger i = 0; i < indexPath.section; i++) {
absolute_position += [collectionView numberOfItemsInSection:i];
}
}
NSInteger relative_position = 0;
// UICollectionViewCell
if ([[collectionView visibleCells] containsObject:cell]) {
relative_position = [[collectionView visibleCells] indexOfObject:cell];
view.relative_position = [NSString stringWithFormat:@"%zd",relative_position];
break;
//NSLog(@"%f---%f",relative_position,absolute_position);
}
}
}
}
current = (UITableViewCell *)current.superview;
}
view.absolute_position = [NSString stringWithFormat:@"%zd",absolute_position];
}
// 将当前UIScrollView数据同步给另外一个otherScrollView
- (void)synchronizePageTimesToOther:(UIView *)otherView {
otherView.pageCtrl.up_slide_times = self.pageCtrl.up_slide_times;
otherView.pageCtrl.down_slide_times = self.pageCtrl.down_slide_times;
otherView.pageCtrl.up_loading_times = self.pageCtrl.up_loading_times;
otherView.pageCtrl.down_loading_times = self.pageCtrl.down_loading_times;
}
/// 判断当前view是否处于屏幕上
/// @param view 指定view
+ (BOOL)viewIsVisible:(UIView *)view {
// 可见
if (!view.window || view.hidden || view.layer.hidden || !view.alpha) {
return NO;
}
UIView *current = view;
while ([current isKindOfClass:[UIView class]]) {
if (current.alpha <= 0 || current.hidden) {
return NO;
}
current = current.superview;
}
// 判断当前View 与 view所在window是否存在交集
CGRect viewRectInWindow = [view convertRect:view.bounds toView:view.window];
BOOL isIntersects = CGRectIntersectsRect(view.window.bounds, viewRectInWindow);
if (isIntersects) {
// 获取View 与 view所在window的相交区域
CGRect intersectRect = CGRectIntersection(view.window.bounds, viewRectInWindow);
if (intersectRect.size.width != 0.f && intersectRect.size.height != 0.f) {
return YES;
}
}
return NO;
}
#pragma mark - GMViewExposureProtocol
- (void)setVisibleView:(BOOL)visibleView {
objc_setAssociatedObject(self, @selector(visibleView), @(visibleView), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)visibleView {
return [objc_getAssociatedObject(self, @selector(visibleView)) boolValue];
}
- (void)setInTime:(NSString *)inTime {
objc_setAssociatedObject(self, @selector(inTime), inTime, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)inTime {
return objc_getAssociatedObject(self, @selector(inTime));
}
- (void)setOutTime:(NSString *)outTime {
objc_setAssociatedObject(self, @selector(outTime), outTime, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)outTime {
return objc_getAssociatedObject(self, @selector(outTime));
}
- (void)setAbsolute_position:(NSString *)absolute_position {
objc_setAssociatedObject(self, @selector(absolute_position), absolute_position, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)absolute_position {
return objc_getAssociatedObject(self, @selector(absolute_position));
}
- (void)setRelative_position:(NSString *)relative_position {
objc_setAssociatedObject(self, @selector(relative_position), relative_position, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)relative_position {
return objc_getAssociatedObject(self, @selector(relative_position));
}
- (void)setPageCtrl:(UIViewController *)pageCtrl {
GMWeakObject *obj = [[GMWeakObject alloc] initWithObj:pageCtrl];
objc_setAssociatedObject(self, @selector(pageCtrl), obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIViewController *)pageCtrl {
GMWeakObject *weakObj = objc_getAssociatedObject(self, @selector(pageCtrl));
if (!weakObj.obj) {
weakObj.obj = [UIView currentControllerForView:self];
}
return weakObj.obj;
}
- (void)setViewName:(NSString *)viewName {
objc_setAssociatedObject(self, @selector(viewName), viewName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)viewName {
return objc_getAssociatedObject(self, @selector(viewName));
}
- (void)setExpoStatus:(GMViewExpoStatus)expoStatus {
objc_setAssociatedObject(self, @selector(expoStatus), @(expoStatus), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (expoStatus == GMViewExpoStatusStart) {
} else if (expoStatus == GMViewExpoStatusEnd) {
self.inTime = @"";
self.outTime = @"";
self.relative_position = @"";
self.absolute_position = @"";
}
}
- (GMViewExpoStatus)expoStatus {
return [objc_getAssociatedObject(self, @selector(expoStatus)) integerValue];
}
- (void)setExtra_param:(NSDictionary *)extra_param {
objc_setAssociatedObject(self, @selector(extra_param), extra_param, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSDictionary *)extra_param {
return objc_getAssociatedObject(self, @selector(extra_param));
}
@end
//
// UIViewController+Tracker.h
// Gengmei
//
// Created by Mikasa on 2019/6/25.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
#import "GMExposureProtocol.h"
#import "GMTrackerProtocol.h"
@interface UIViewController (Tracker)<GMPageExposureProtocol,GMCpcProtocol,UIScrollViewDelegate>
@end
//
// UIViewController+Tracker.m
// Gengmei
//
// Created by Mikasa on 2019/6/25.
// Copyright © 2019 更美互动信息科技有限公司. All rights reserved.
//
#import "UIViewController+Tracker.h"
#import "GMExposureManager.h"
#import "NSObject+Swizzle.h"
#import <objc/runtime.h>
@implementation UIViewController (Tracker)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self startViewPageExpoTracker];
});
}
+ (void)startViewPageExpoTracker {
[self swizzleInstanceMethod:@selector(viewDidLoad) withSelector:@selector(swizzle_viewDidLoad)];
[self swizzleInstanceMethod:@selector(viewDidAppear:) withSelector:@selector(swizzle_viewDidAppear:)];
[self swizzleInstanceMethod:@selector(viewDidDisappear:) withSelector:@selector(swizzle_viewDidDisappear:)];
}
- (void)swizzle_viewDidLoad {
[self swizzle_viewDidLoad];
if (self.needExpo) {
self.view.pageCtrl = self;
}
}
- (void)swizzle_viewDidAppear:(BOOL)animated {
[self swizzle_viewDidAppear:animated];
if (self.needExpo) {
self.view.pageCtrl = self;
[GMExposureManager fetchViewForVisibleState:self.view trackerType:GMViewTrackerTypeUIViewDidAppear recursive:YES];
if (self.needImmediatelySend) {
[GMExposureManager.sharedManager uploadExposureDataForPage:self];
}
}
}
- (void)swizzle_viewDidDisappear:(BOOL)animated {
if (self.needExpo) {
[[GMExposureManager sharedManager] endExpoTrcakerForPageCtrl:self];
}
[self swizzle_viewDidDisappear:animated];
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if (scrollView.pageCtrl.needExpo) {
scrollView.preContentOffsetY = scrollView.contentOffset.y;
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (scrollView.pageCtrl.needExpo && !decelerate) {
[scrollView calculateUpOrDownTimes];
[GMExposureManager fetchViewForVisibleState:scrollView trackerType:GMViewTrackerTypeUIScrollDragging recursive:YES];
if (self.needImmediatelySend) {
[GMExposureManager.sharedManager endExpoTrcakerForPageCtrl:self];
}
}
//NSLog(@"%@:%zd--%zd",scrollView.pageCtrl,scrollView.pageCtrl.down_slide_times,scrollView.pageCtrl.up_slide_times);
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView.pageCtrl.needExpo) {
[scrollView calculateUpOrDownTimes];
[GMExposureManager fetchViewForVisibleState:scrollView trackerType:GMViewTrackerTypeUIScrollDragging recursive:YES];
if (self.needImmediatelySend) {
[GMExposureManager.sharedManager endExpoTrcakerForPageCtrl:self];
}
}
}
#pragma mark - GMPageExposureProtocol
- (void)setNeedImmediatelySend:(BOOL)needImmediatelySend {
objc_setAssociatedObject(self, @selector(needImmediatelySend), @(needImmediatelySend), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)needImmediatelySend {
return [objc_getAssociatedObject(self, @selector(needImmediatelySend)) boolValue];
}
- (void)setNeedExpo:(BOOL)needExpo {
objc_setAssociatedObject(self, @selector(needExpo), @(needExpo), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)needExpo {
return [objc_getAssociatedObject(self, @selector(needExpo)) boolValue];
}
- (void)setUp_slide_times:(NSInteger)up_slide_times {
objc_setAssociatedObject(self, @selector(up_slide_times), @(up_slide_times), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)up_slide_times {
return [objc_getAssociatedObject(self, @selector(up_slide_times)) integerValue];
}
- (void)setDown_slide_times:(NSInteger)down_slide_times {
objc_setAssociatedObject(self, @selector(down_slide_times), @(down_slide_times), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)down_slide_times {
return [objc_getAssociatedObject(self, @selector(down_slide_times)) integerValue];
}
- (void)setUp_loading_times:(NSInteger)up_loading_times {
objc_setAssociatedObject(self, @selector(up_loading_times), @(up_loading_times), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)up_loading_times {
return [objc_getAssociatedObject(self, @selector(up_loading_times)) integerValue];
}
- (void)setDown_loading_times:(NSInteger)down_loading_times {
objc_setAssociatedObject(self, @selector(down_loading_times), @(down_loading_times), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)down_loading_times {
return [objc_getAssociatedObject(self, @selector(down_loading_times)) integerValue];
}
- (void)setExtra_param:(NSDictionary *)extra_param {
objc_setAssociatedObject(self, @selector(extra_param), extra_param, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSDictionary *)extra_param {
return objc_getAssociatedObject(self, @selector(extra_param));
}
@end
......@@ -11,12 +11,7 @@
#import "PhobosConfig.h"
#import "Phobos.h"
#import "PhobosUtil.h"
#ifdef APPSTORE
#define GMExactExposureApi @"https://log.igengmei.com/log/precise_exposure"
#else
#define GMExactExposureApi @"http://log.test.igengmei.com/log/precise_exposure"
#endif
#import "GMExposureManager.h"
@implementation PhobosSendManager
......@@ -44,7 +39,7 @@
}
}];
}
NSString *exposureAPI = GMExactExposureApi;
NSString *exposureAPI = [GMExposureManager sharedManager].uploadExposureAPI;
NSArray *exposureArray = [GMCache fetchObjectAtDocumentPathWithkey:[PhobosUtil MD5String:exposureAPI]];
if (exposureArray.count > 0) {
/** 获取非灰度下发送失败的埋点,进行发送 */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment