setNeedsDisplay

本文详细介绍了iOS开发中UIView和CALayer中的setNeedsDisplay方法。该方法用于触发视图的刷新和重绘,文中通过具体实例展示了如何使用此方法,并探讨了其在不同情况下的行为差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在CALayer - 1中我们了解到setNeedsDisplay这个方法,而其实UIView 和 CALayer都是存在这个方法的!我们来看看这个方法:无论在UIView 或者 CALayer上,都是提及这个方法的作用:刷新,重绘 视图内容contents

来看看简单的例子:

UIView:

头文件:

#import <UIKit/UIKit.h>

@interface LBSetNeedsDisplayUIViewViewController : UIViewController

@end


@interface LBNeedsDisView : UIView

@property (nonatomic, assign) BOOL hasRandomColor;

@end

实现文件:

#import "UIColor+RandomColor.h"
#import "LBSetNeedsDisplayUIViewViewController.h"

@interface LBSetNeedsDisplayUIViewViewController ()

@property (nonatomic, strong) LBNeedsDisView* needsDisView;

@end

@implementation LBSetNeedsDisplayUIViewViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.needsDisView = [[LBNeedsDisView alloc] init];
    [self.needsDisView setFrame:CGRectMake(0, 0, 100, 100)];
    [self.needsDisView setCenter:self.view.center];
    [self.view addSubview:self.needsDisView];
    
    UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setFrame:CGRectMake(0, 0, 50, 50)];
    [button setCenter:CGPointMake(self.view.center.x, button.center.y)];
    [button setBackgroundColor:[UIColor yellowColor]];
    [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

- (void)buttonClicked:(id)sender
{
    self.needsDisView.hasRandomColor = YES;
    
    [self.needsDisView setNeedsDisplay];
}

@end


#pragma LBNeedsDisView

@implementation LBNeedsDisView

- (id)init
{
    self = [super init];
    
    if (self)
    {
        _hasRandomColor = NO;
    }
    
    return self;
}

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    
    
    if (_hasRandomColor)
    {
        self.backgroundColor = [UIColor randomColor]; //随机刷新颜色
    }
    
}

@end


同理地,CALayer:

头文件:

#import <UIKit/UIKit.h>

@interface LBSetNeedsDisplayCALayerViewController : UIViewController

@end

@interface LBNeedsDisplayLayer : CALayer

@property (nonatomic, assign) BOOL hasRandomColor;

@end

实现文件:

#import "UIColor+RandomColor.h"
#import "LBSetNeedsDisplayCALayerViewController.h"

@interface LBSetNeedsDisplayCALayerViewController ()

@property (nonatomic, strong) LBNeedsDisplayLayer* layer;

@end

@implementation LBSetNeedsDisplayCALayerViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.layer = [LBNeedsDisplayLayer layer];
    self.layer.backgroundColor = [UIColor brownColor].CGColor;
    self.layer.bounds = CGRectMake(100, 100, 200, 150);
    [self.view.layer addSublayer:self.layer];
    
    
    UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setFrame:CGRectMake(0, 0, 50, 50)];
    [button setCenter:CGPointMake(self.view.center.x, button.center.y)];
    [button setBackgroundColor:[UIColor yellowColor]];
    [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

- (void)buttonClicked:(id)sender
{
    self.layer.hasRandomColor = YES;
    
    [self.layer setNeedsDisplay];
}

@end


@implementation LBNeedsDisplayLayer

- (id)init
{
    self = [super init];
    
    if (self)
    {
        _hasRandomColor = NO;
    }
    
    return self;
}

- (void)drawInContext:(CGContextRef)ctx
{
    [super drawInContext:ctx];
    
    if (_hasRandomColor)
    {
        self.backgroundColor = [UIColor randomColor].CGColor;
    }
}

@end

实现方式几乎一样哈!!

这里提几点:

1.UIView的drawRect在自定义UIView的子类初始化重载(重写)的同时也会调用,但是CALayer不会

2.两者通过setNeedsDisplay都会调用对应的drawRect: 和 DrawInContext: , 贴切刷新的意思

3.两者的rect如果是zero , 都不能响应这个方法,应该没有刷新和重绘的必要,两者都有局部重绘的方法

4.CALayer如果不用drawInContext:而采用代理实现的话,setNeedsDisplay是响应代理的实现方法:drawLayer: inContext:

5.线程上是异步执行,不会阻塞线程,并且在runloop最后一个阶段执行(有待验证)


非常常见的用法,用在set方法里面,每次调用set属性方法的时候及时刷新:

更新属性后,调用setNeedsDisplay

- (void)setType:(JSBubbleMessageType)newType
{
    type = newType;
    [self setNeedsDisplay];
}

在drawRect:方法里面相应地判断:

- (void)drawRect:(CGRect)frame
{
    [super drawRect:frame];

    	
    if (self.mediaType == JSBubbleMediaTypeText)
	{
		//do something.
	}

}

上面两个例子就是这样的形式来编写的



参考:http://www.cnblogs.com/ydhliphonedev/archive/2011/08/12/2135627.html



下篇章会讲解和这个方法非常相像的setNeedsLayout

<think>我们正在讨论的是iOS中使用Objective-C进行UI绘制,特别是实现一个简易容器的绘制方法。根据用户的问题,他可能想要了解如何自定义一个UIView,并在其中绘制一些图形(比如矩形作为容器)以及如何添加一些交互功能。 参考之前的引用[1]和[2],我们可以知道: 引用[1]提到使用Core Graphics绘制柱状图并添加交互功能,这涉及到自定义视图的绘制和触摸事件处理。 引用[2]展示了一个自定义的渐变色进度条视图(HLProgressView),它继承自UIView,通过重写drawRect:方法来实现绘制。 因此,我们可以通过以下步骤来实现一个简易容器: 1. 创建一个继承自UIView的子类。 2. 重写drawRect:方法,使用Core Graphics进行绘制。 3. 可以添加一些属性来控制容器的外观(比如颜色、边框等)。 4. 如果需要交互,可以重写触摸事件处理方法。 下面我们详细说明如何实现一个简单的容器视图,它绘制一个圆角矩形,并可以填充颜色和边框。 步骤: 1. 创建自定义视图类,例如命名为SimpleContainerView。 2. 在SimpleContainerView.m文件中重写drawRect:方法。 3. 在drawRect:方法中,使用UIBezierPath或Core Graphics函数绘制一个圆角矩形。 4. 设置填充颜色和描边颜色,并绘制路径。 代码示例: SimpleContainerView.h 文件: ```objective-c #import <UIKit/UIKit.h> @interface SimpleContainerView : UIView // 可以添加一些自定义属性,比如容器的填充颜色、边框颜色、边框宽度等 @property (nonatomic, strong) UIColor *fillColor; @property (nonatomic, strong) UIColor *strokeColor; @property (nonatomic, assign) CGFloat strokeWidth; @property (nonatomic, assign) CGFloat cornerRadius; @end ``` SimpleContainerView.m 文件: ```objective-c #import "SimpleContainerView.h" @implementation SimpleContainerView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 设置默认属性 _fillColor = [UIColor lightGrayColor]; _strokeColor = [UIColor darkGrayColor]; _strokeWidth = 2.0; _cornerRadius = 10.0; } return self; } // 重写drawRect:方法 - (void)drawRect:(CGRect)rect { [super drawRect:rect]; // 创建圆角矩形路径 UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, self.strokeWidth/2, self.strokeWidth/2) cornerRadius:self.cornerRadius]; // 设置填充颜色 [self.fillColor setFill]; [roundedRect fill]; // 设置描边属性 [self.strokeColor setStroke]; roundedRect.lineWidth = self.strokeWidth; [roundedRect stroke]; } @end ``` 使用说明: 在需要使用的地方,创建SimpleContainerView的实例,并设置其frame,然后添加到父视图上。也可以通过设置fillColor、strokeColor等属性来自定义外观。 例如,在某个ViewController中: ```objective-c SimpleContainerView *container = [[SimpleContainerView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)]; container.fillColor = [UIColor blueColor]; container.strokeColor = [UIColor redColor]; container.strokeWidth = 3.0; container.cornerRadius = 15.0; [self.view addSubview:container]; ``` 如果需要添加交互,比如点击容器后改变颜色,可以重写触摸事件方法。例如: ```objective-c // 在SimpleContainerView.m中添加 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 触摸开始,可以改变颜色 self.fillColor = [UIColor greenColor]; [self setNeedsDisplay]; // 重绘 } - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 触摸结束,恢复颜色 self.fillColor = [UIColor blueColor]; [self setNeedsDisplay]; } ``` 注意:在drawRect:方法中,我们使用了UIBezierPath,它是Core Graphics的一个封装,使用起来更简单。如果需要更复杂的绘制,可以直接使用Core Graphics的API。 引用[1]中提到的Core Graphics Programming Guide是官方文档,对于深入理解绘制机制非常有帮助[^1]。 总结:通过自定义UIView并重写drawRect:方法,我们可以实现各种自定义绘制。同时,通过添加触摸事件处理,可以为视图增加交互能力。 §§相关问题§§ 1. 如何在iOS中绘制更复杂的图形,比如带有渐变色填充的图形? 2. 如何优化自定义视图的性能,尤其是在绘制复杂图形时? 3. 除了drawRect:方法,还有哪些方式可以实现自定义UI(例如使用CALayer)? [^1]: Core Graphics Programming Guide提供了关于Quartz 2D绘图引擎的详细信息,包括路径、颜色、渐变和变换等。</think>在 iOS 开发中使用 Objective-C 进行 UI 绘制,特别是实现简易容器的绘制,主要通过 Core Graphics 框架实现。以下是完整的实现方法和步骤: ### 1. 创建自定义 UIView 子类 首先创建一个继承自 `UIView` 的自定义类,这是绘制自定义 UI 的基础: ```objective-c // SimpleContainerView.h #import <UIKit/UIKit.h> @interface SimpleContainerView : UIView @property (nonatomic, strong) UIColor *containerColor; @property (nonatomic, assign) CGFloat cornerRadius; @end ``` ### 2. 实现绘制逻辑(核心) 在 `.m` 文件中重写 `drawRect:` 方法,使用 Core Graphics 进行绘制: ```objective-c // SimpleContainerView.m #import "SimpleContainerView.h" @implementation SimpleContainerView - (void)drawRect:(CGRect)rect { [super drawRect:rect]; // 获取当前绘图上下文 CGContextRef context = UIGraphicsGetCurrentContext(); // 设置容器属性(默认值) UIColor *fillColor = self.containerColor ?: [UIColor lightGrayColor]; CGFloat radius = self.cornerRadius > 0 ? self.cornerRadius : 8.0; // 创建圆角矩形路径 UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius]; // 设置填充颜色 CGContextSetFillColorWithColor(context, fillColor.CGColor); // 添加路径到上下文并填充 CGContextAddPath(context, roundedRect.CGPath); CGContextFillPath(context); // 添加边框(可选) CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor); CGContextSetLineWidth(context, 1.0); CGContextAddPath(context, roundedRect.CGPath); CGContextStrokePath(context); } @end ``` ### 3. 使用自定义容器视图 在视图控制器中使用自定义容器: ```objective-c // ViewController.m #import "ViewController.h" #import "SimpleContainerView.h" @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 创建并配置容器 SimpleContainerView *container = [[SimpleContainerView alloc] initWithFrame:CGRectMake(50, 100, 200, 150)]; container.containerColor = [UIColor systemBlueColor]; container.cornerRadius = 12.0; container.backgroundColor = [UIColor clearColor]; // 确保透明 // 添加子视图到容器(演示容器功能) UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 160, 30)]; label.text = @"容器中的内容"; label.textAlignment = NSTextAlignmentCenter; [container addSubview:label]; [self.view addSubview:container]; } @end ``` ### 关键绘制技术说明 1. **绘图上下文获取**: ```objective-c CGContextRef context = UIGraphicsGetCurrentContext(); ``` 获取当前视图的绘图上下文,所有绘制操作都在此上下文中进行[^1]。 2. **路径创建**: ```objective-c UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius]; ``` 使用 `UIBezierPath` 创建圆角矩形路径,这是容器的基本形状[^1]。 3. **填充与描边**: ```objective-c CGContextSetFillColorWithColor(context, fillColor.CGColor); CGContextAddPath(context, roundedRect.CGPath); CGContextFillPath(context); ``` 设置填充颜色并填充路径,类似地可以使用 `CGContextStrokePath` 描边路径。 4. **属性自定义**: ```objective-c @property (nonatomic, strong) UIColor *containerColor; @property (nonatomic, assign) CGFloat cornerRadius; ``` 通过公开属性实现外观定制,调用 `setNeedsDisplay` 可触发重绘。 ### 高级功能扩展 1. **添加渐变效果**: ```objective-c // 在 drawRect: 中添加 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGFloat locations[] = {0.0, 1.0}; NSArray *colors = @[(__bridge id)[UIColor whiteColor].CGColor, (__bridge id)fillColor.CGColor]; CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations); CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(0, rect.size.height), 0); CGGradientRelease(gradient); CGColorSpaceRelease(colorSpace); ``` 2. **响应触摸事件**: ```objective-c // 在 SimpleContainerView.m 中添加 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 触摸反馈效果 [UIView animateWithDuration:0.1 animations:^{ self.alpha = 0.7; }]; } - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [UIView animateWithDuration:0.2 animations:^{ self.alpha = 1.0; }]; } ``` ### 性能优化建议 1. **避免频繁重绘**: - 只在必要时调用 `setNeedsDisplay` - 使用 `CGRectIntersectsRect` 检查是否需要重绘可见区域 2. **使用 CAShapeLayer 替代**: 对于静态形状,使用 CALayer 更高效: ```objective-c CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = roundedRect.CGPath; shapeLayer.fillColor = fillColor.CGColor; [self.layer addSublayer:shapeLayer]; ``` 3. **离屏渲染优化**: ```objective-c self.layer.shouldRasterize = YES; self.layer.rasterizationScale = [UIScreen mainScreen].scale; ``` ### 实际应用场景 1. 自定义弹出框容器 2. 卡片式UI组件 3. 分组内容容器 4. 自定义按钮背景 5. 数据可视化图表容器[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值