GaiaXiOS/GaiaXiOS/Component/Node/GXViewNode.m (256 lines of code) (raw):

// // GXViewNode.m // GaiaXiOS // // Copyright (c) 2021, Alibaba Group Holding Limited. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #import "GXViewNode.h" #import <GaiaMotionCurve/GaiaMotionCurve.h> #import "GXLottieAniamtionProtocal.h" #import "GXRegisterCenter.h" #import "GXAnimationModel.h" #import "NSDictionary+GX.h" #import "GXBizHelper.h" #import "NSArray+GX.h" #import "UIView+GX.h" #import "GXUtils.h" #import "GXView.h" @interface GXViewNode () { //是否手动触发 BOOL _isLottie; BOOL _isTrigger; BOOL _userEnable; } //动画属性 @property (nonatomic, copy) NSString *lottieName; //动画属性 @property (nonatomic, strong) GXAnimationModel *animationModel; //动画view @property (nonatomic, strong) UIView<GXLottieAniamtionProtocal> *animationView; @end @implementation GXViewNode - (UIView *)creatView{ UIView *view = self.associatedView; if (!view) { view = [[GXView alloc] initWithFrame:CGRectZero]; view.gxNode = self; view.gxNodeId = self.nodeId; view.gxBizId = self.templateItem.bizId; view.gxTemplateId = self.templateItem.templateId; view.gxTemplateVersion = self.templateItem.templateVersion; self.associatedView = view; //支持渐变背景 self.isSupportGradientBgColor = YES; self.isSupportShadow = YES; //设置通知 if (self.animation) { view.userInteractionEnabled = _userEnable; } } return view; } - (void)renderView:(UIView *)view{ //判断是否相等,更新frame if (!CGRectEqualToRect(view.frame, self.frame)) { view.frame = self.frame; } //设置属性 view.alpha = self.opacity; view.clipsToBounds = self.clipsToBounds; //设置背景色 if (self.linearGradient) { [self setupGradientBackground:view]; } else { [self setupNormalBackground:view]; } //设置圆角 [self setupCornerRadius:view]; //毛玻璃 [self setupBlur:view]; //设置阴影 // [self setupShadow:view]; } #pragma mark - 绑定数据 - (void)bindData:(NSDictionary *)data{ //赋值 if ([GXUtils isDictionary:data]) { //处理extend NSDictionary *extend = [data gx_dictionaryForKey:@"extend"]; if (extend) { [self handleExtend:extend isCalculate:NO]; } //处理无障碍 [self setupAccessibilityInfo:data]; } } #pragma mark - 计算高度 - (void)calculateWithData:(NSDictionary *)data{ //赋值 if ([GXUtils isValidDictionary:data]) { NSDictionary *extend = [data gx_dictionaryForKey:@"extend"]; if (extend) { [self handleExtend:extend isCalculate:YES]; } } } #pragma mark - 扩展处理 - (void)handleExtend:(NSDictionary *)extend isCalculate:(BOOL)isCalculate{ //更新布局属性 & 标记 BOOL isMark = [self updateLayoutStyle:extend]; //更新普通属性 if (!isCalculate) { [self updateNormalStyle:extend isMark:isMark]; } //确认属性发生变化,更新布局 if (isMark) { //更改style + rustPtr [self.style updateRustPtr]; [self setStyle:self.style]; //标记dirty [self markDirty]; //重新刷新布局标识 self.templateContext.isNeedLayout = YES; } } - (BOOL)updateLayoutStyle:(NSDictionary *)styleInfo{ BOOL isMark = [super updateLayoutStyle:styleInfo]; //毛玻璃 NSString *backdropFilter = [styleInfo gx_stringForKey:@"backdrop-filter"]; if (backdropFilter.length ) { self.backdropFilter = backdropFilter; if (!isMark) { [self setupBlur:self.associatedView]; } } return isMark; } #pragma mark - 属性解析 //读取属性 - (void)configureStyleInfo:(NSDictionary *)styleJson{ [super configureStyleInfo:styleJson]; //动画 if (self.animation) { //设置当前view是否可以交互 NSString *type = [self.animation gx_stringForKey:@"type"]; if ([type isEqualToString:@"lottie"]) { _userEnable = NO; _isLottie = YES; } else { _userEnable = YES; _isLottie = NO; } //设置是否手动触发 _isTrigger = [self.animation gx_boolForKey:@"trigger"]; } } #pragma mark - 绑定动画 - (void)bindAnimation:(NSDictionary *)data{ //获取动画数据 & 动画view NSDictionary *animationDict = (NSDictionary *)data; //动画处理 if ([GXUtils isValidDictionary:animationDict] && (self.style.styleModel.display == DisplayFlex)) { if ([animationDict objectForKey:@"lottieAnimator"] || [animationDict objectForKey:@"propAnimatorSet"]) { //加载动画 [self setupGaiaXAnimation:animationDict]; } else { //隐藏动画 if (_animationView) { _animationView.hidden = YES; [_animationView gx_stop]; } } } else { //隐藏动画 if (_animationView) { _animationView.hidden = YES; [_animationView gx_stop]; } } } #pragma mark - new animation - (void)setupGaiaXAnimation:(NSDictionary *)animationInfo{ //更新model if (!_animationModel) { _animationModel = [[GXAnimationModel alloc] init]; } [_animationModel setupAnimationInfo:animationInfo frame:self.frame]; //判断类型 NSString *animationType = _animationModel.type; if ([animationType isEqualToString:@"lottie"]) { //lottie动画 [self lottieAnimation:_animationModel]; } else if ([animationType isEqualToString:@"prop"]){ //属性动画 [self propAnimation:_animationModel]; } } //lottie动画 - (void)lottieAnimation:(GXAnimationModel *)animationModel{ //判断lottie的class是否存在 Class lottieViewClass = TheGXRegisterCenter.lottieViewClass; if (lottieViewClass == nil) { return; } //读取属性 BOOL trigger = animationModel.trigger; BOOL state = animationModel.state; //手动触发动画,state = false if (trigger && !state) { return; } BOOL loop = animationModel.lottieAnimator.loop; BOOL isLocal = animationModel.lottieAnimator.isLocal; NSString *lottieUrl = animationModel.lottieAnimator.value; //正在播放就直接return if (_animationView && !_animationView.hidden && [_animationView gx_isAnimationPlaying] && [_lottieName isEqualToString:lottieUrl]) { return; } //动画停止 _animationView.hidden = YES; [_animationView gx_stop]; //动画判断 _lottieName = lottieUrl; if (!_lottieName.length) { return; } //创建view if (_animationView == nil) { _animationView = [[lottieViewClass alloc] initWithFrame:self.associatedView.bounds]; _animationView.translatesAutoresizingMaskIntoConstraints = YES; _animationView.contentMode = UIViewContentModeScaleAspectFit; _animationView.userInteractionEnabled = NO; [self.associatedView insertSubview:_animationView atIndex:0]; } //执行animation if (_animationView) { //显示动画 & 更新frame _animationView.hidden = NO; _animationView.frame = self.associatedView.bounds; //动画参数 NSMutableDictionary *animationInfo = [NSMutableDictionary dictionary]; [animationInfo gx_setObject:@(isLocal) forKey:@"isLocal"]; [animationInfo gx_setObject:_lottieName forKey:@"lottieUrl"]; [animationInfo gx_setObject:@((loop ? -1 : 0)) forKey:@"loopCount"]; //执行动画 GXWeakSelf(self); [_animationView gx_playAnimation:animationInfo completion:^(BOOL finished) { GXStrongSelf(self) NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary gx_setObject:animationModel.animationInfo forKey:@"animationInfo"]; [dictionary gx_setObject:@(finished) forKey:@"animationFinished"]; [self animationDidFinished:dictionary]; }]; } } //标准曲线动画 - (void)propAnimation:(GXAnimationModel *)animationModel{ //子动画属性 NSArray *animators = animationModel.propAnimatorSet.animators; NSInteger count = animators.count; if (count == 0) { return; } //读取属性 BOOL trigger = animationModel.trigger; BOOL state = animationModel.state; //手动触发动画 & state = false if (trigger && !state) { return; } //动画执行顺序 NSString *ordering = animationModel.propAnimatorSet.ordering; [self.associatedView.layer gmc_cancelAllAnimations]; //执行动画 if (count == 1 || ![ordering isEqualToString:@"sequentially"]) { //动画组 NSMutableArray *animationModels = [NSMutableArray array]; for (int i = 0; i < count; i ++) { GXPropAnimationModel *propAnimator = [animationModel.propAnimatorSet.animators gx_objectAtIndex:i]; GMCModel *model = [GMCModel modelWithKeyPath:propAnimator.propName duration:propAnimator.duration delay:propAnimator.delay repeatCount:propAnimator.loopCount autoReverse:propAnimator.autoReverse curveType:propAnimator.curveType fromValue:propAnimator.valueFrom toValue:propAnimator.valueTo]; [animationModels gx_addObject:model]; } //添加动画 GXWeakSelf(self) [self.associatedView.layer gmc_animateWithGMCModels:animationModels completion:^(BOOL finished) { //动画结束的回调 if (finished) { GXStrongSelf(self); [self animationDidFinished:animationModel.animationInfo]; } }]; } else { //动画组 NSMutableArray *animationModels = [NSMutableArray array]; for (int i = 0; i < count; i ++) { GXPropAnimationModel *propAnimator = [animationModel.propAnimatorSet.animators gx_objectAtIndex:i]; GMCModel *model = [GMCModel modelWithKeyPath:propAnimator.propName duration:propAnimator.duration delay:propAnimator.delay repeatCount:propAnimator.loopCount autoReverse:propAnimator.autoReverse curveType:propAnimator.curveType fromValue:propAnimator.valueFrom toValue:propAnimator.valueTo]; [animationModels gx_addObject:model]; } //顺序执行动画 GXWeakSelf(self) [self.associatedView.layer gmc_serialAnimationWithGMCModels:animationModels completion:^(BOOL finished) { //动画结束的回调 if (finished) { GXStrongSelf(self); [self animationDidFinished:animationModel.animationInfo]; } }]; } } //动画结束 - (void)animationDidFinished:(NSDictionary *)animationInfo{ id <GXEventProtocal> eventListener = self.templateContext.templateData.eventListener; if (eventListener && [eventListener respondsToSelector:@selector(gx_animationDidFinished:)]) { [eventListener gx_animationDidFinished:animationInfo]; } } @end