前言
视图置顶关于 bringSubviewToFront 和view.layer.zPosition的选择
- 使用bringSubviewToFront方法需要在重新刷新界面结构层次的时候调用;
- 使用view.layer.zPosition方法会获取不到view的点击事件
应用场景
1 、比如让日期控件置于窗口的最顶层
2、悬浮按钮(支持拖曳)
I 、bringSubviewToFront的用法
- 让日期控件置于窗口的最顶层
PGDatePickManager https://kunnan.blog.csdn.net/article/details/119381308
@implementation PGDatePickManager (ios12) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSArray *selStringsArray = @[@"viewWillLayoutSubviews"]; // @"reloadRowsAtIndexPaths:withRowAnimation:", @"deleteRowsAtIndexPaths:withRowAnimation:", @"insertRowsAtIndexPaths:withRowAnimation:"]; [selStringsArray enumerateObjectsUsingBlock:^(NSString *selString, NSUInteger idx, BOOL *stop) { NSString *mySelString = [@"sd_" stringByAppendingString:selString]; Method originalMethod = class_getInstanceMethod(self, NSSelectorFromString(selString)); Method myMethod = class_getInstanceMethod(self, NSSelectorFromString(mySelString)); method_exchangeImplementations(originalMethod, myMethod); }]; }); } - (void)sd_viewWillLayoutSubviews{ [self sd_viewWillLayoutSubviews]; [UIApplication.sharedApplication.delegate.window bringSubviewToFront:self.view.superview]; }
- listTableView
[self.superview.window addSubview:self.listTableView]; /// 避免被其他子视图遮盖住 [self.superview.window bringSubviewToFront:self.listTableView]; CGRect frame = CGRectMake(CGRectGetMinX(self.frame), CGRectGetMaxY(self.frame), CGRectGetWidth(self.frame), 0); //坐标转换 CGRect convertRect= [self.superview convertRect:frame toView:self.superview.window]; [self.listTableView setFrame:convertRect];
II、同级Layer改变显示顺序
- self.view.layer.zPosition
self.view.layer.zPosition = MAXFLOAT; 999
III 案例:悬浮按钮(支持拖曳)
下级订货单关于悬浮按钮的相关需求:
1、存在“待发货”记录时,显示“一键发货”按钮 点击一键发货:实现待发货的分配记录,都更新为待收货 2、存在“待收货”记录时,显示“一键代收货”按钮 点击一键代收货:实现待发货的分配记录,都更新为“已收货”
3.1 原理
1 、bringSubviewToFront 2、添加移动手势可以拖动 3、使用谓词进行判断是否存在特定条件的数据
//添加移动手势可以拖动 self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragAction:)]; self.panGestureRecognizer.minimumNumberOfTouches = 1; self.panGestureRecognizer.maximumNumberOfTouches = 1; self.panGestureRecognizer.delegate = self; [self addGestureRecognizer:self.panGestureRecognizer];
3.2 用法
@property (strong, nonatomic) KNFrontV * orangeView; @end @implementation QCTRecordViewController - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [self.view bringSubviewToFront:self.orangeView]; [self.orangeView layoutIfNeeded]; self.orangeView.layer.cornerRadius =self.orangeView.height *0.5; } - (KNFrontV *)orangeView{ if (nil == _orangeView) { KNFrontV *tmpView = [[KNFrontV alloc] initWithFrame:CGRectMake(0, 0 , kAdjustRatio(53), kAdjustRatio(53))]; _orangeView = tmpView; [self.view addSubview:_orangeView]; __weak __typeof__(self) weakSelf = self; tmpView.button.titleLabel.numberOfLines = 0; tmpView.button.titleLabel.textAlignment = NSTextAlignmentCenter; tmpView.button.titleLabel.font = [UIFont systemFontOfSize:15.0]; [tmpView.button setTitle:@"一键\n发货" forState:UIControlStateNormal];// 发货 购买\n开店数 tmpView.backgroundColor = rgb(255,54,87); // // tmpView.layer.cornerRadius = 14;// layoutsubview //设置显示图片方式一: // tmpView.imageView.image = [UIImage imageNamed:@"icon_dayin"]; //设置显示图片方式二: // [logoView.button setBackgroundImage:[UIImage imageNamed:@"logo1024"] forState:UIControlStateNormal]; [_orangeView mas_makeConstraints:^(MASConstraintMaker *make) { make.size.mas_equalTo(CGSizeMake(kAdjustRatio(53), kAdjustRatio(53))); make.right.offset(kAdjustRatio(-20)); make.bottom.offset(kAdjustRatio(-90)); }]; tmpView.clickDragViewBlock = ^(KNFrontV *dragView){ [weakSelf setupclickDragViewBlock]; }; } return _orangeView; } - (void)setupclickDragViewBlock{ }
- KNFrontV的定义
// // KNFrontV.h // Housekeeper // // Created by mac on 2021/5/6. // Copyright © 2021 https://kunnan.blog.csdn.net/ . All rights reserved. // #import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN // 拖曳view的方向 typedef NS_ENUM(NSInteger, KNDragDirection) { KNDragDirectionAny, /**< 任意方向 */ KNDragDirectionHorizontal, /**< 水平方向 */ KNDragDirectionVertical, /**< 垂直方向 */ }; @interface KNFrontV : UIView /** 是不是能拖曳,默认为YES YES,能拖曳 NO,不能拖曳 */ @property (nonatomic,assign) BOOL dragEnable; /** 活动范围,默认为父视图的frame范围内(因为拖出父视图后无法点击,也没意义) 如果设置了,则会在给定的范围内活动 如果没设置,则会在父视图范围内活动 注意:设置的frame不要大于父视图范围 注意:设置的frame为0,0,0,0表示活动的范围为默认的父视图frame,如果想要不能活动,请设置dragEnable这个属性为NO */ @property (nonatomic,assign) CGRect freeRect; /** 拖曳的方向,默认为any,任意方向 */ @property (nonatomic,assign) KNDragDirection dragDirection; /** contentView内部懒加载的一个UIImageView 开发者也可以自定义控件添加到本view中 注意:最好不要同时使用内部的imageView和button */ @property (nonatomic,strong) UIImageView *imageView; /** contentView内部懒加载的一个UIButton 开发者也可以自定义控件添加到本view中 注意:最好不要同时使用内部的imageView和button */ @property (nonatomic,strong) UIButton *button; /** 是不是总保持在父视图边界,默认为NO,没有黏贴边界效果 isKeepBounds = YES,它将自动黏贴边界,而且是最近的边界 isKeepBounds = NO, 它将不会黏贴在边界,它是free(自由)状态,跟随手指到任意位置,但是也不可以拖出给定的范围frame */ @property (nonatomic,assign) BOOL isKeepBounds; /** 点击的回调block */ @property (nonatomic,copy) void(^clickDragViewBlock)(KNFrontV *dragView); /** 开始拖动的回调block */ @property (nonatomic,copy) void(^beginDragBlock)(KNFrontV *dragView); /** 拖动中的回调block */ @property (nonatomic,copy) void(^duringDragBlock)(KNFrontV *dragView); /** 结束拖动的回调block */ @property (nonatomic,copy) void(^endDragBlock)(KNFrontV *dragView); @end NS_ASSUME_NONNULL_END
KNFrontV的完整实现请看CSDN原文:https://blog.csdn.net/z929118967/article/details/105298711
3.3 使用 NSPredicate
判断是否存在“待收货”记录
/** 下级订货单 1、存在“待发货”记录时,显示“一键发货”按钮 点击一键发货:实现待发货的分配记录,都更新为待收货 2、存在“待收货”记录时,显示“一键代收货”按钮 点击一键代收货:实现待发货的分配记录,都更新为“已收货” 我的订货单 存在“待收货”记录时,显示“一键收货”按钮 点击一键收货:实现待发货的分配记录,都更新为“已收货” */ - (void) updateorangeView{ // if(![self isShoworangeView]){ self.orangeView.hidden = YES; }else{ [self orangeView]; self.orangeView.hidden = NO; [self.orangeView.button setTitle:self.orangeViewM.showStr forState:UIControlStateNormal];// 发货 购买\n开店数 } } - (BOOL)isShoworangeView{ self.orangeViewM = [KNFrontVM new]; if(self.model.isLowerOrder){// 下级 // 1、存在“待发货”记录时,显示“一键发货”按钮// 优先显示 NSPredicate* predicate = [NSPredicate predicateWithFormat:@"receivingState == %@", @"0"]; NSArray *arFiltered = [ self.Detailmodels filteredArrayUsingPredicate:predicate];//以一定的条件(特定日期)过滤maTemp数组,即进行大数据搜索。 if(arFiltered.count>0){ self.orangeViewM.isShow = YES; self.orangeViewM.showStr = @"一键\n发货"; self.orangeViewM.type = ReceivingDelieverEnum4Deliever; return self.orangeViewM.isShow; } // 2、存在“待收货”记录时,显示“一键代收货”按钮 predicate = [NSPredicate predicateWithFormat:@"receivingState == %@", @"1"]; arFiltered = [ self.Detailmodels filteredArrayUsingPredicate:predicate];// if(arFiltered.count>0){ self.orangeViewM.isShow = YES; self.orangeViewM.showStr = @"一键\n代收货"; self.orangeViewM.type = ReceivingDelieverEnum4ProReceiving; } }else{// 本级 // 存在“待收货”记录时,显示“一键收货”按钮 NSPredicate* predicate = [NSPredicate predicateWithFormat:@"receivingState == %@", @"1"]; NSArray *arFiltered = [ self.Detailmodels filteredArrayUsingPredicate:predicate];//以一定的条件(特定日期)过滤maTemp数组,即进行大数据搜索。 if(arFiltered.count>0){ self.orangeViewM.isShow = YES; self.orangeViewM.showStr = @"一键\n收货"; self.orangeViewM.type = ReceivingDelieverEnum4Receiving; } } return self.orangeViewM.isShow; }
see also
- iOS视图置顶的应用:适配iOS12系统上日期控件被筛选视图遮挡问题
- iOS数据搜索技巧之【利用正则表达式进行匹配查找数据 】1、对聊天记录的关键词进行监控 2、谓词在正则表达式的应用
- iOS Predicate Format String Syntax 【NSPredicate在正则表达式的应用】
- iOS NSPredicate的应用【从数组搜索特定条件的元素、NSPredicate在正则表达式的应用】
- 推荐使用
[[UIApplication sharedApplication].delegate window]
获取window
在执行
didFinishLaunchingWithOptions:
这个代理方法时,调用[self.window makeKeyAndVisible]
;方法之前,通过[UIApplication sharedApplication].keyWindow 方法获取不到window, 但是无论何时都能获取到delegate.window。
- 在获取到window时最好使用[[UIApplication sharedApplication].delegate window]获取window
- 不要在keywindow为nil的时候给window上添加代码,例如添加弹窗。