美文网首页
Graver源码分析(2)——交互

Graver源码分析(2)——交互

作者: 无悔zero | 来源:发表于2021-06-07 10:42 被阅读0次

在上篇文章Graver绘制中,我们知道了Graver把复杂的界面变成了图片,那局部怎么交互呢?比如我们点击图片中的文字。

  1. 首先WMMutableAttributedItem是最小绘制单元,也就是它绘制我们点击的文字,WMMutableAttributedItem里有WMGTextAttachment数组,WMGTextAttachment是附件(图片、事件响应),由WMGTextAttachment来处理事件:
  1. 我们先从添加入手,主要是保存targetaction
WMMutableAttributedItem
@implementation WMGTextAttachment
...

- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
{
    _target = target;
    _selector = action;
    
    _responseEvent = (_target && _selector) && [_target respondsToSelector:_selector];//文本组件是否响应事件
}
  1. 点击响应时,通过touchesBegan判定激活区域:
@implementation WMGMixedView
...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [_textDrawer touchesBegan:touches withEvent:event];//事件传递
    //判断激活区
    if (!_textDrawer.pressingActiveRange)
    {
        [super touchesBegan:touches withEvent:event];//父类传递
    }
}
@implementation WMGTextDrawer
...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UIView *contextView = [self eventDelegateContextView];
    
    if (!contextView) { ... }
    
    const CGPoint location = [[touches anyObject] locationInView:contextView];
    const CGPoint layoutLocation = [self convertPointToLayout:location offsetPoint:_drawOrigin];
    //当前被激活的区域
    id<WMGActiveRange> activeRange = [self rangeInRanges:[self eventDelegateActiveRanges] forLayoutLocation:layoutLocation];
    
    if (activeRange) {
        [self setPressingActiveRange:activeRange];//设置
        [contextView setNeedsDisplay];
    }
    
    _touchesBeginPoint = location;
}
  1. 接着在touchesEnded时进行响应:
@implementation WMGTextDrawer
...
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (_lastTouchEndedTimeStamp!= event.timestamp) {
        self.savedPressingActiveRange = nil;
        _lastTouchEndedTimeStamp = event.timestamp;
        if (self.pressingActiveRange) {
            
            id<WMGActiveRange> activeRange = self.pressingActiveRange;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self eventDelegateDidPressActiveRange:activeRange];
            });
            ...
        }
    }
}
...
- (void)eventDelegateDidPressActiveRange:(id<WMGActiveRange>)activeRange
{
    if (_eventDelegateHas.didPressActiveRange) {
        [_eventDelegate textDrawer:self didPressActiveRange:activeRange];
    }
}
  1. 最后通过代理响应方法:
@implementation WMGMixedView
...
- (void)textDrawer:(WMGTextDrawer *)textDrawer didPressActiveRange:(id<WMGActiveRange>)activeRange
{
    if (activeRange.type == WMGActiveRangeTypeAttachment) {
        WMGTextAttachment *att = (WMGTextAttachment *)activeRange.bindingData;//附件(图片,事件响应)
        [att handleEvent:self];//处理事件
    }
}
@implementation WMGTextAttachment
...
- (void)handleEvent:(id)sender
{
    if (_target && _selector) {
        if ([_target respondsToSelector:_selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            [_target performSelector:_selector withObject:sender];//响应方法
#pragma clang diagnostic pop
        }
    }
}
  1. 我们回头来看看第2步,如果没有激活区域,那便会进行事件传递:
@implementation WMGCanvasControl
...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    _touchInside = YES;
    _tracking = [self beginTrackingWithTouch:touch withEvent:event];//是否需要追逐事件
    
    self.highlighted = YES;
    
    if (_tracking)
    {
        UIControlEvents currentEvents = UIControlEventTouchDown;
        
        if (touch.tapCount > 1) {
            currentEvents |= UIControlEventTouchDownRepeat;
        }
        
        [self _sendActionsForControlEvents:currentEvents withEvent:event];//事件的传递
    }
}
...
- (void)_sendActionsForControlEvents:(UIControlEvents)controlEvents withEvent:(UIEvent *)event
{
    for(__WMGCanvasControlTargetAction *t in [self _targetActions])
    {
        if(t.controlEvents == controlEvents)
        {
            if(t.target && t.action)
            {
                [self sendAction:t.action to:t.target forEvent:nil];
            }
        }
    }
}
...
- (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
    [[UIApplication sharedApplication] sendAction:action to:target from:self forEvent:event];
}
  • 到这里我们可以发现,它是模仿了系统的事件传递:
给点击方法断点测试 点击UIButton发生的事件传递
  • 补充

Graver为了更好地在tableView上使用,提供了一个基类WMGBaseViewModelWMGBaseViewModel负责与Graver相关的业务层操作:

@interface WMGBaseViewModel : NSObject{
...
@property (nonatomic, strong, readonly) NSMutableArray <WMGBaseCellData *> *arrayLayouts;//包装数据提供给cell

// 网络请求返回的错误
@property (nonatomic, strong, readonly) NSError *error;

// 当前列表状态,详见WMGListState说明
@property (nonatomic, assign) WMGListState listState;

// 松耦合方式连接engine和viewModel,通过桥接模式实现
@property (nonatomic, strong) WMGBaseEngine *engine;

数据最终包装成WMGBaseCellData提供给cell

@interface WMGBaseCellData : NSObject

// cell宽度
@property (nonatomic, assign) CGFloat cellWidth;

// cell高度
@property (nonatomic, assign) CGFloat cellHeight;

// 视图分割线样式
@property (nonatomic, assign) WMGCellSeparatorLineStyle separatorStyle;

// 根据该属性值反射UI数据对应的视图Class,子类可以通过覆盖方式指定,默认取当前类同名对应的Cell
// 例如: WMGListCellData -> WMGListCell
@property (nonatomic, assign, readonly) Class cellClass;

// UI数据对应的业务数据
@property (nonatomic, weak) id <WMGClientData> metaData;

@end

还有WMGBaseEngine,负责数据的请求、处理,需要继承重写相关方法:

/*
 整体负责
 1.数据存储 Insert Delete Update Select操作
 2.网络请求、数据解析
 */
@interface WMGBaseEngine : NSObject{ ... }
// 结果集,业务列表数据、是否有下一页、当前处于第几页的封装,适用于流式列表结构
@property (nonatomic, strong, readonly, nullable) WMGResultSet *resultSet;
// 载入状态,用于标识当前网络请求的载入状态
@property (nonatomic, assign, readonly) WMGEngineLoadState loadState;
//reload请求
- (void)reloadDataWithParams:(nullable NSDictionary *)params completion:(nullable WMGEngineLoadCompletion)completion;
//loadmore请求
- (void)loadMoreDataWithParams:(nullable NSDictionary *)params completion:(nullable WMGEngineLoadCompletion)completion;
//insert请求
- (void)insertDataWithParams:(nullable NSDictionary *)params withIndex:(NSUInteger)insertIndex completion:(nullable WMGEngineLoadCompletion)completion;
@end

// 对数据的增删改查
@interface WMGBaseEngine (DataOperation)
//添加一条数据
- (void)addItem:(WMGBusinessModel *)item;
//插入一条数据
- (void)insertItem:(WMGBusinessModel *)item atIndex:(NSUInteger)index;
//删除一条数据
- (void)deleteItem:(WMGBusinessModel *)item;
//删除所有业务数据
- (void)deleteAllItems;
@end

简单来说,之前文章说的WMGAsyncDrawView负责UI层,WMGBaseViewModel负责业务层。

相关文章

网友评论

      本文标题:Graver源码分析(2)——交互

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