iOS视图置顶方案

1,129 阅读3分钟

这是我参与8月更文挑战的第6天,活动详情查看: 8月更文挑战” juejin.cn/post/698796…

前言

视图置顶关于 bringSubviewToFront 和view.layer.zPosition的选择

  1. 使用bringSubviewToFront方法需要在重新刷新界面结构层次的时候调用;

  2. 使用view.layer.zPosition方法会获取不到view的点击事件

应用场景

1 、比如让日期控件置于窗口的最顶层

2、悬浮按钮(支持拖曳)

I 、bringSubviewToFront的用法

  • 让日期控件置于窗口的最顶层

PGDatePickManager kunnan.blog.csdn.net/article/det…

@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原文:blog.csdn.net/z929118967/…

或者关注公众号:iOS逆向

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;
    
    
    
}


IV iOS视图置顶的应用

本文以pod 'PGDatePicker' ,'2.6.9'·为例子

问题:iOS12系统上日期视图被筛选视图遮挡

思路:利用运行时API修改第三方SDK的内部实现

mp.weixin.qq.com/s/rT4Iu_Fb8…

see also

  • 推荐使用[[UIApplication sharedApplication].delegate window]获取window

在执行 didFinishLaunchingWithOptions: 这个代理方法时,调用[self.window makeKeyAndVisible];方法之前,通过[UIApplication sharedApplication].keyWindow 方法获取不到window, 但是无论何时都能获取到delegate.window。

  1. 在获取到window时最好使用[[UIApplication sharedApplication].delegate window]获取window

  2. 不要在keywindow为nil的时候给window上添加代码,例如添加弹窗。