iOS开发-CAShapeLayer与UIBezierPath实现微信首页的下拉菜单效果

news2024/12/25 12:35:25

iOS开发-CAShapeLayer与UIBezierPath实现微信首页的下拉菜单效果

之前开发中遇到需要使用实现微信首页的下拉菜单效果。用到了CAShapeLayer与UIBezierPath绘制菜单外框。

在这里插入图片描述

一、效果图

在这里插入图片描述

二、CAShapeLayer与UIBezierPath

2.1、CAShapeLayer是什么?

CAShapeLayer继承自CALayer,可使用CALayer的所有属性
CAShapeLayer需要和UIBezierPath绘制图形。

创建shapeLayer

// 创建 shapeLayer
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc]init];
[self.view.layer addSublayer:shapeLayer];
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 5;

2.2、UIBezierPath是什么?

UIBezierPath即贝塞尔曲线
UIBezierPath 类允许你在自定义的 View 中绘制和渲染由直线和曲线组成的路径

+ (instancetype)bezierPath;
+ (instancetype)bezierPathWithRect:(CGRect)rect;
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;

三、绘制箭头

在弹出的菜单弹出时候会显示箭头,这里使用UIBezierPath和CAShapeLayer绘制箭头。

#pragma mark - DrawShapeLayer
- (void)drawShapeLayer {
    
    CGFloat startPointX = self.contentStartPoint.x;
    CGFloat startPointY = self.contentStartPoint.y;
    
    CGFloat width = CGRectGetWidth(self.contentBGImageView.frame);
    CGFloat height = CGRectGetHeight(self.contentBGImageView.frame);

    self.path = [UIBezierPath bezierPath]; // 创建路径
    [self.path moveToPoint:CGPointMake(startPointX, startPointY)]; // 设置起始点
    
    [self.path addLineToPoint:CGPointMake(startPointX + 5.0, kAnchorHeight)];

    [self.path addLineToPoint:CGPointMake(width - 5.0, kAnchorHeight)];
    
    [self.path addArcWithCenter:CGPointMake(width - 5.0, kAnchorHeight + 5.0) radius:5 startAngle:KCP*3/2 endAngle:2*KCP clockwise:YES]; // 绘制一个圆弧
    
    [self.path addLineToPoint:CGPointMake(width, height - 5.0)];
    
    [self.path addArcWithCenter:CGPointMake(width - 5.0, height - 5.0) radius:5 startAngle:0 endAngle:KCP/2 clockwise:YES]; // 绘制一个圆弧
    [self.path addLineToPoint:CGPointMake(5, height)];
    [self.path addArcWithCenter:CGPointMake(5.0, height-5.0) radius:5 startAngle:KCP/2 endAngle:KCP clockwise:YES]; // 绘制一个圆弧
    
    [self.path addLineToPoint:CGPointMake(0.0, kAnchorHeight + 5.0)];
    
    [self.path addArcWithCenter:CGPointMake(5, kAnchorHeight + 5) radius:5 startAngle:KCP endAngle:KCP*3/2 clockwise:YES]; // 绘制一个圆弧
    
    [self.path addLineToPoint:CGPointMake(startPointX - 5.0, kAnchorHeight)];
    [self.path addLineToPoint:CGPointMake(startPointX, startPointY)];

    self.path.lineWidth     = 2.f;
    self.path.lineCapStyle  = kCGLineCapRound;
    self.path.lineJoinStyle = kCGLineCapRound;
    
    [self.path closePath]; // 封闭未形成闭环的路径
    
    UIGraphicsBeginImageContext(self.contentBGImageView.bounds.size);
    [self.path stroke];
    UIGraphicsEndImageContext();

    self.shapeLayer.path = self.path.CGPath;
}

四、点击触摸的mask处理

当下拉菜单显示后,MaskView上实现touchesBegan可以点击触摸可以隐藏菜单,

INNoteZoneOptionMaskView.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@protocol INNoteZoneOptionMaskViewDelegate;
@interface INNoteZoneOptionMaskView : UIView

@property (nonatomic, weak) id<INNoteZoneOptionMaskViewDelegate>maskDelegate;

@end

@protocol INNoteZoneOptionMaskViewDelegate <NSObject>

- (void)optionMaskTouched;

@end

INNoteZoneOptionMaskView.m

#import "INNoteZoneOptionMaskView.h"

@implementation INNoteZoneOptionMaskView

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    // 1.自己先处理事件...
    if (self.maskDelegate && [self.maskDelegate respondsToSelector:@selector(optionMaskTouched)]) {
        [self.maskDelegate optionMaskTouched];
    }
    // 2.再调用系统的默认做法,再把事件交给上一个响应者处理
    [super touchesBegan:touches withEvent:event];
}

@end

五、菜单显示

控件显示在keyWindow上

[[UIApplication sharedApplication].keyWindow addSubview:self];

显示动画

- (void)showOption {
    
    self.hidden = NO;
    
    CGPoint anchorPoint = CGPointMake(self.contentStartPoint.x/self.contentBGImageView.frame.size.width, 0.0);
    [self resetAnChorPoint:self.contentBGImageView anchorPoint:anchorPoint];
    self.contentBGImageView.transform = CGAffineTransformMakeScale(0.1, 0.1);
    
    [UIView animateWithDuration:0.25 animations:^{
        self.contentBGImageView.transform = CGAffineTransformMakeScale(1.0, 1.0);
        self.contentBGImageView.alpha = 1.0;
    } completion:^(BOOL finished) {
        
    }];
}

隐藏菜单

- (void)dismissOption {
    [UIView animateWithDuration:0.25 animations:^{
        self.contentBGImageView.transform = CGAffineTransformMakeScale(0.1, 0.1);
        self.contentBGImageView.alpha = 0.0;
    } completion:^(BOOL finished) {
        self.hidden = YES;
        [self removeFromSuperview];
    }];
}

五、菜单完整代码

菜单完整代码如下

INNoteZoneOptionView.h

#import <UIKit/UIKit.h>

/**
 元素的item
 */
typedef void(^OptionTouchBlock)(void);
@interface INNoteZoneOptionItem : NSObject

@property (nonatomic, strong) UIImage *iconImage;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, copy) OptionTouchBlock block;

@end


/**
 按钮控件
 */
@interface INNoteZoneOptionButton : UIControl

@property (nonatomic, strong) UIImage *iconImage;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, assign) BOOL showLine;
@property (nonatomic, strong) INNoteZoneOptionItem *item;

@end


/**
 点击➕号的选项操作
 */
@interface INNoteZoneOptionView : UIView

- (instancetype)initWithFrame:(CGRect)frame anchorPoint:(CGPoint)anchorPoint items:(NSArray *)items;

- (void)showOption;

- (void)dismissOption;

@end

INNoteZoneOptionView.m

#import "INNoteZoneOptionView.h"
#import "UIColor+Addition.h"
#import "UIImageView+WebCache.h"
#import "UIImage+YYAdd.h"
#import "INNoteZoneOptionMaskView.h"

#define KCP 3.1415926

static CGFloat kOpContentWidth = 130.0;
static CGFloat kOpItemHeight = 50.0;

static CGFloat kOpIconSize = 16.0;
static CGFloat kOpMidPadding = 10.0;
static CGFloat kAnchorHeight = 5.0;

static CGFloat kBtnMidPadding = 15.0;

static CGFloat kBtnPadding = 2.0;
static CGFloat kLineHeight = 1.0;

/**
 元素的item
 */
@interface INNoteZoneOptionItem ()


@end


@implementation INNoteZoneOptionItem


@end


/**
 按钮控件
 */
@interface INNoteZoneOptionButton ()

@property (nonatomic, strong) UIImageView *bgImageView;
@property (nonatomic, strong) UIImageView *iconImageView;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIImageView *lineImageView;

@end

@implementation INNoteZoneOptionButton

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self addSubview:self.bgImageView];
        [self.bgImageView addSubview:self.iconImageView];
        [self.bgImageView addSubview:self.titleLabel];
        [self.bgImageView addSubview:self.lineImageView];
        self.showLine = NO;
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    self.bgImageView.frame = self.bounds;
    self.iconImageView.frame = CGRectMake(kBtnMidPadding, (CGRectGetHeight(self.bounds) - kOpIconSize)/2, kOpIconSize, kOpIconSize);
    self.titleLabel.frame = CGRectMake(CGRectGetMaxX(self.iconImageView.frame) + kBtnMidPadding, 0.0, CGRectGetWidth(self.bgImageView.frame) - (CGRectGetMaxX(self.iconImageView.frame) + kBtnMidPadding*2), CGRectGetHeight(self.bounds));
    self.lineImageView.frame = CGRectMake(0.0, CGRectGetHeight(self.bgImageView.frame) - kLineHeight, CGRectGetWidth(self.bgImageView.frame), kLineHeight);
}

- (void)setIconImage:(UIImage *)iconImage {
    _iconImage = iconImage;
    self.iconImageView.image = iconImage;
    [self setNeedsLayout];
}

- (void)setTitle:(NSString *)title {
    _title = (title?title:@"");
    self.titleLabel.text = _title;
    [self setNeedsLayout];
}

- (void)setShowLine:(BOOL)showLine {
    _showLine = showLine;
    self.lineImageView.hidden = !showLine;
    [self setNeedsLayout];
}

- (void)setHighlighted:(BOOL)highlighted {
    [super setHighlighted:highlighted];
    if (highlighted) {
        self.bgImageView.backgroundColor = [UIColor colorWithHexString:@"f4f4f4"];
    } else {
        self.bgImageView.backgroundColor = [UIColor whiteColor];
    }
}

#pragma mark - SETTER/GETTER
- (UIImageView *)iconImageView {
    if (!_iconImageView) {
        _iconImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _iconImageView.contentMode = UIViewContentModeScaleAspectFit;
    }
    return _iconImageView;
}

- (UILabel *)titleLabel {
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
        _titleLabel.font = [UIFont boldSystemFontOfSize:14];
        _titleLabel.textColor = [UIColor colorWithHexString:@"131619"];
        _titleLabel.backgroundColor = [UIColor clearColor];
    }
    
    return _titleLabel;
}

- (UIImageView *)bgImageView {
    if (!_bgImageView) {
        _bgImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _bgImageView.backgroundColor = [UIColor whiteColor];
        _bgImageView.layer.cornerRadius = 2.0;
        _bgImageView.layer.masksToBounds = YES;
    }
    return _bgImageView;
}

- (UIImageView *)lineImageView {
    if (!_lineImageView) {
        _lineImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _lineImageView.backgroundColor = [UIColor colorWithHexString:@"f1f1f1"];
    }
    return _lineImageView;
}

@end

/**
 点击➕号的选项操作
 */
@interface INNoteZoneOptionView ()<INNoteZoneOptionMaskViewDelegate>

@property (nonatomic, strong) INNoteZoneOptionMaskView *maskBGView;
@property (nonatomic, strong) UIImageView *contentBGImageView;
@property (nonatomic, strong) CAShapeLayer *shapeLayer;
@property (nonatomic, strong) UIBezierPath *path;

@property (nonatomic) CGPoint anchorPoint;
@property (nonatomic) CGPoint contentStartPoint;
@property (nonatomic, strong) NSArray *items;

@end

@implementation INNoteZoneOptionView

- (instancetype)initWithFrame:(CGRect)frame anchorPoint:(CGPoint)anchorPoint items:(NSArray *)items {
    self = [super initWithFrame:frame];
    if (self) {
        
        self.anchorPoint = anchorPoint;
        self.items = items;
        
        [self addSubview:self.maskBGView];
        [self addSubview:self.contentBGImageView];
        [self.contentBGImageView.layer addSublayer:self.shapeLayer];
        
        [[UIApplication sharedApplication].keyWindow addSubview:self];
        self.frame = [UIScreen mainScreen].bounds;
        self.hidden = YES;
        
        [self layoutOptionSubviews];

        [self configContentPoint];
        
        [self drawShapeLayer];
        
        [self setupOptionButtons];
    }
    return self;
}

- (void)configContentPoint {
    CGFloat scale = self.anchorPoint.x/self.frame.size.width;
    CGFloat contentWidth = CGRectGetWidth(self.contentBGImageView.frame);
    self.contentStartPoint = CGPointMake(scale*contentWidth - kOpMidPadding, 0.0);
}

- (void)layoutOptionSubviews {
    self.maskBGView.frame = self.bounds;
    self.contentBGImageView.frame = CGRectMake(CGRectGetWidth(self.bounds) - kOpContentWidth - kOpMidPadding, self.anchorPoint.y, kOpContentWidth, self.items.count*kOpItemHeight + kAnchorHeight + 2*kBtnPadding);
    self.shapeLayer.frame = self.contentBGImageView.bounds;
}

#pragma mark - Setup Buttons
- (void)setupOptionButtons {
    NSInteger index = 0;
    for (INNoteZoneOptionItem *item in self.items) {
        INNoteZoneOptionButton *button = [[INNoteZoneOptionButton alloc] initWithFrame:CGRectZero];
        button.frame = CGRectMake(kBtnPadding, kBtnPadding + kAnchorHeight + index*kOpItemHeight, CGRectGetWidth(self.contentBGImageView.frame) - 2*kBtnPadding, kOpItemHeight);
        
        button.iconImage = item.iconImage;
        button.title = item.title;
        button.item = item;
        [self.contentBGImageView addSubview:button];
        [button addTarget:self action:@selector(optionButtonAction:) forControlEvents:UIControlEventTouchUpInside];
        
        button.showLine = YES;
        if (index == self.items.count - 1) {
            button.showLine = NO;
        }
        
        index++;
    }
}

- (void)optionButtonAction:(INNoteZoneOptionButton *)button {
    NSLog(@"点击栏目");
    if (button.item && button.item.block) {
        button.item.block();
    }
}

#pragma mark - Show AND Dismiss
- (void)showOption {
    
    self.hidden = NO;
    
    CGPoint anchorPoint = CGPointMake(self.contentStartPoint.x/self.contentBGImageView.frame.size.width, 0.0);
    [self resetAnChorPoint:self.contentBGImageView anchorPoint:anchorPoint];
    self.contentBGImageView.transform = CGAffineTransformMakeScale(0.1, 0.1);
    
    [UIView animateWithDuration:0.25 animations:^{
        self.contentBGImageView.transform = CGAffineTransformMakeScale(1.0, 1.0);
        self.contentBGImageView.alpha = 1.0;
    } completion:^(BOOL finished) {
        
    }];
}

- (void)dismissOption {
    [UIView animateWithDuration:0.25 animations:^{
        self.contentBGImageView.transform = CGAffineTransformMakeScale(0.1, 0.1);
        self.contentBGImageView.alpha = 0.0;
    } completion:^(BOOL finished) {
        self.hidden = YES;
        [self removeFromSuperview];
    }];
}

#pragma mark - ResetAnChorPoint
/**
 设置动画图钉位置
 
 @param view subView
 */
- (void)resetAnChorPoint:(UIView *)view anchorPoint:(CGPoint)anchorPoint {
    CGPoint oldAnchorPoint = view.layer.anchorPoint;
    view.layer.anchorPoint = anchorPoint;
    [view.layer setPosition:CGPointMake(view.layer.position.x + view.layer.bounds.size.width * (view.layer.anchorPoint.x - oldAnchorPoint.x), view.layer.position.y + view.layer.bounds.size.height * (view.layer.anchorPoint.y - oldAnchorPoint.y))];
}


#pragma mark - INNoteZoneOptionMaskViewDelegate
- (void)optionMaskTouched {
    [self dismissOption];
}

#pragma mark - SETTER/GETTER
- (INNoteZoneOptionMaskView *)maskBGView {
    if (!_maskBGView) {
        _maskBGView = [[INNoteZoneOptionMaskView alloc] initWithFrame:CGRectZero];
        _maskBGView.backgroundColor = [UIColor clearColor];
        _maskBGView.userInteractionEnabled = YES;
        _maskBGView.maskDelegate = self;
    }
    return _maskBGView;
}

- (UIImageView *)contentBGImageView {
    if (!_contentBGImageView) {
        _contentBGImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _contentBGImageView.userInteractionEnabled = YES;
        _contentBGImageView.backgroundColor = [UIColor clearColor];
    }
    return _contentBGImageView;
}

- (CAShapeLayer *)shapeLayer {
    if (!_shapeLayer) {
        _shapeLayer = [CAShapeLayer layer];
        _shapeLayer.fillColor = [UIColor whiteColor].CGColor;
        //设置线条的宽度和颜色
        _shapeLayer.lineWidth = 1.0f;
        _shapeLayer.strokeColor = [UIColor colorWithHexString:@"9bb9ef" alpha:0.55].CGColor;
        _shapeLayer.shadowColor = [UIColor colorWithHexString:@"9bb9ef"].CGColor;
        _shapeLayer.shadowOffset = CGSizeMake(-3, 3);
        _shapeLayer.shadowOpacity = 0.3;
        _shapeLayer.shadowRadius = 3.0;
    }
    return _shapeLayer;
}

#pragma mark - DrawShapeLayer
- (void)drawShapeLayer {
    
    CGFloat startPointX = self.contentStartPoint.x;
    CGFloat startPointY = self.contentStartPoint.y;
    
    CGFloat width = CGRectGetWidth(self.contentBGImageView.frame);
    CGFloat height = CGRectGetHeight(self.contentBGImageView.frame);

    self.path = [UIBezierPath bezierPath]; // 创建路径
    [self.path moveToPoint:CGPointMake(startPointX, startPointY)]; // 设置起始点
    
    [self.path addLineToPoint:CGPointMake(startPointX + 5.0, kAnchorHeight)];

    [self.path addLineToPoint:CGPointMake(width - 5.0, kAnchorHeight)];
    
    [self.path addArcWithCenter:CGPointMake(width - 5.0, kAnchorHeight + 5.0) radius:5 startAngle:KCP*3/2 endAngle:2*KCP clockwise:YES]; // 绘制一个圆弧
    
    [self.path addLineToPoint:CGPointMake(width, height - 5.0)];
    
    [self.path addArcWithCenter:CGPointMake(width - 5.0, height - 5.0) radius:5 startAngle:0 endAngle:KCP/2 clockwise:YES]; // 绘制一个圆弧
    [self.path addLineToPoint:CGPointMake(5, height)];
    [self.path addArcWithCenter:CGPointMake(5.0, height-5.0) radius:5 startAngle:KCP/2 endAngle:KCP clockwise:YES]; // 绘制一个圆弧
    
    [self.path addLineToPoint:CGPointMake(0.0, kAnchorHeight + 5.0)];
    
    [self.path addArcWithCenter:CGPointMake(5, kAnchorHeight + 5) radius:5 startAngle:KCP endAngle:KCP*3/2 clockwise:YES]; // 绘制一个圆弧
    
    [self.path addLineToPoint:CGPointMake(startPointX - 5.0, kAnchorHeight)];
    [self.path addLineToPoint:CGPointMake(startPointX, startPointY)];

    self.path.lineWidth     = 2.f;
    self.path.lineCapStyle  = kCGLineCapRound;
    self.path.lineJoinStyle = kCGLineCapRound;
    
    [self.path closePath]; // 封闭未形成闭环的路径
    
    UIGraphicsBeginImageContext(self.contentBGImageView.bounds.size);
    [self.path stroke];
    UIGraphicsEndImageContext();

    self.shapeLayer.path = self.path.CGPath;
}

@end

六、使用显示下拉菜单

使用显示下拉菜单代码,根据anchorPoint确定显示的箭头位置

#pragma mark - INNoteZoneNavbarViewDelegate
- (void)addButtonDidAction:(UIButton *)button {
    CGRect btnRect = [button convertRect:button.bounds toView:[UIApplication sharedApplication].keyWindow];
    
    __weak typeof(self) weakSelf = self;
    INNoteZoneOptionItem *postItem = [[INNoteZoneOptionItem alloc] init];
    postItem.iconImage = [UIImage imageNamed:@"ic_op_editpost"];
    postItem.title = @"发布帖子";
    postItem.block = ^{
        NSLog(@"发布帖子");
    };
    
    INNoteZoneOptionItem *scanItem = [[INNoteZoneOptionItem alloc] init];
    scanItem.iconImage = [UIImage imageNamed:@"ic_op_scan"];
    scanItem.title = @"扫一扫";
    scanItem.block = ^{
        NSLog(@"扫一扫");
    };
    
    INNoteZoneOptionItem *calItem = [[INNoteZoneOptionItem alloc] init];
    calItem.iconImage = [UIImage imageNamed:@"ic_op_cal"];
    calItem.title = @"日历";
    calItem.block = ^{
        NSLog(@"日历");
    };
    
    INNoteZoneOptionItem *pubWordItem = [[INNoteZoneOptionItem alloc] init];
    pubWordItem.iconImage = [UIImage imageNamed:@"ic_op_edit"];
    pubWordItem.title = @"发布美句";
    pubWordItem.block = ^{
        NSLog(@"发布美句");
    };

    INNoteZoneOptionView *optionView = [[INNoteZoneOptionView alloc] initWithFrame:CGRectZero anchorPoint:CGPointMake(CGRectGetMidX(btnRect), CGRectGetMaxY(btnRect)) items:@[postItem,scanItem,calItem,pubWordItem]];
    [optionView showOption];
}

七、小结

iOS开发-CAShapeLayer与UIBezierPath实现微信首页的下拉菜单效果。用到了CAShapeLayer与UIBezierPath绘制菜单外框。
学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/807132.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Nacos启动时遇到的一些错误

当我们双击nacos的bin目录下的statup.cmd启动Nacos时&#xff0c;发现报以下错误&#xff1a; [dba-load-error] load jdbc.properties error 报错的原因是&#xff1a;数据库找不到&#xff0c;没有导入。 解决方法步骤&#xff1a; ①在安装的nacos的conf目录下&#xff0…

C++:类和对象(下)---对类和对象深入一些的理解

文章目录 构造函数&#xff1f;初始化列表explicit关键字 匿名对象 构造函数&#xff1f; 初始化列表 前面已然介绍过构造函数&#xff0c;但并未完全结束&#xff0c;构造函数有很多种写法&#xff0c;有带缺省参数的&#xff0c;有全缺省的&#xff0c;不带缺省参数的…但用…

Jenkins从配置到实战(二) - Jenkins的Master-Slave分布式构建

前言 Jenkins的Master-Slave分布式构建&#xff0c;就是通过将构建过程分配到从属Slave节点上&#xff0c;从而减轻Master节点的压力&#xff0c;而且可以同时构建多个&#xff0c;有点类似负载均衡的概念。简单理解就是&#xff0c;将Jenkins服务器上的构建任务分配到其他机器…

GitLab开启双端认证并登录GitLab

GitLab开启双端认证并登录GitLab 1.介绍双端认证 单重认证——密码验证&#xff0c;这极其容易出现密码被盗&#xff0c;密码泄露等危险事件。 于是为了提高安全性&#xff0c;就出现了双因素认证&#xff0c;多因素认证。登录的时候不仅要输入账号和密码还需要输入一个验证码…

C++模板进价

本期我们来学习C模板的进价内容&#xff0c;没有看过初阶的同学建议先看看初阶内容 (26条消息) C模板初阶_KLZUQ的博客-CSDN博客 目录 非类型模板参数 模板特化 函数模板特化 类模板特化 模板分离编译 模板总结 我们之前一直说我们写模板时&#xff0c;typename和class没…

FPGA2-采集OV5640乒乓缓存后经USB3.0发送到上位机显示

1.场景 基于特权A7系列开发板&#xff0c;采用OV5640摄像头实时采集图像数据&#xff0c;并将其经过USB3.0传输到上位机显示。这是验证数据流能力的很好的项目。其中&#xff0c;用到的软件版本&#xff0c;如下表所示&#xff0c;基本的硬件情况如下。该项目对应FPGA工程源码…

【雕爷学编程】Arduino动手做(129)---TTS文字转语音合成模块2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

【iOS】KVC KVO 总结

文章目录 KVC1. KVC赋值原理 setValue:forKey:2. KVC取值原理 valueForKey:3. 注意4. KVC的批量存值和取值 KVO 使用1. KVO的介绍2. KVO监听的步骤注册监听监听实现移除监听例子 3. KVO的传值4. KVO注意5. KVO的使用场景 KVO原理1. KVO的本质是改变了setter方法的调用2. _NSSet…

【图论】树上差分(边差分)

一.简介 其实点差分和边差分区别不大。 点差分中&#xff0c;d数组存储的是树上的节点 边差分中&#xff0c;d数组存储的是当前节点到父节点的那条边的差分值。 指定注意的是&#xff1a;边差分中因为根连的父节点是虚点&#xff0c;所以遍历结果时应当忽略&#xff01; 二…

西安科技大学:融合传统与创新的学府之旅

文章目录 一、引言二、历史与发展三、学校特色四、学科建设五、校园环境与设施六、合作交流七、未来发展与展望 一、引言 西安科技大学历史悠久&#xff0c;底蕴深厚。学校办学历史可以追溯到1895年成立的北洋大学工学院采矿冶金科&#xff0c;1938年迁并于西北工学院矿冶系&a…

网络编程、网络编程的三要素、TCP/UDP通信、三次握手和四次挥手

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaweb 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 网络编程 一、初始网络编程1.1什么是网络编程1.2BS/CS的优…

时序预测 | MATLAB实现NARX-ANFIS时间序列预测

时序预测 | MATLAB实现NARX-ANFIS时间序列预测 目录 时序预测 | MATLAB实现NARX-ANFIS时间序列预测效果一览基本介绍研究内容程序设计参考资料效果一览

JS判断类型的方法和对应的局限性(typeof、instanceof和Object.prototype.toString.call()的用法)

JS判断类型的方法和对应的局限性(typeof、instanceof和Object.prototype.toString.call()的用法&#xff09; 一、typeof 返回&#xff1a; 该方法返回小写字符串表示检测数据属于什么类型&#xff0c;例如&#xff1a; 检测函数返回function 可判断的数据类型&#xff1a…

【程序员面试金典】02.07. 链表相交

题目 解题思路 Code Java public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA null || headB null) return null;ListNode a headA;ListNode b headB;while (a ! b ) {a a ! null ? a.next : headB; b b ! null ? b.next : headA; …

MD-MTSP:成长优化算法GO求解多仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、成长优化算法GO 成长优化算法&#xff08;Growth Optimizer&#xff0c;GO&#xff09;由Qingke Zhang等人于2023年提出&#xff0c;该算法的设计灵感来源于个人在成长过程中的学习和反思机制。学习是个人通过从外部世界获取知识而成长的过程&#xff0c;反思是检查个体自…

cmake 配置Visual studio的调试命令

配置代码如截图&#xff1a; set_property(TARGET ${TARGET_NAME} PROPERTY VS_DEBUGGER_COMMAND "./consoleTest.exe") set_property(TARGET ${TARGET_NAME} PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "./config/labelDriver.cfg") set_propert…

【LeetCode每日一题】——84.柱状图中最大的矩形

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 栈 二【题目难度】 困难 三【题目编号】 84.柱状图中最大的矩形 四【题目描述】 给定 n 个…

【LeetCode】142.环形链表Ⅱ

题目 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部…

WPF线程使用详解:提升应用性能和响应能力

在WPF应用程序开发中&#xff0c;线程的合理使用是保证应用性能和响应能力的关键。WPF提供了多种线程处理方式&#xff0c;包括UI线程、后台线程、Task/Async Await和BackgroundWorker。这些方式与传统的Thread类相比&#xff0c;更加适用于WPF框架&#xff0c;并能够简化线程操…

RTPSv2.2(中文版)

实时发布订阅协议 &#xff08;RTPS&#xff09; DDS互操作性 有线协议规范 V2.2 &#xff08;2014-09-01正式发布&#xff09; https://www.omg.org/spec/DDSI-RTPS/2.2/PDF 目 录 1 范围Scope 9 2 一致性Conformance 9 3 参考文献References 9 4 术语和定义Terms a…