Quartz2d 和实例方法
Quartz2d and Instance Method
我尝试编写自己的自定义 class 来做一些二维的东西。 (TwoDOperations.h 和 TwoDOperations.m)。以下是我的文件。
--------TwoDOperations.h------
@interface TwoDOperations : UIView
- (void)drawRect:(CGRect)rect withscene:(UIViewController *)sender;
- (void)drawRectV2:(CGRect)rect;
-(instancetype)init;
@end
--------TwoDOperations.m------
#import "TwoDOperations.h"
@implementation TwoDOperations
-(instancetype)init{
self=[super init];
if (self)
{
}
return self;
}
- (void)drawRect:(CGRect)rect withscene:(UIViewController *)sender{
UIView *myBox = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 300, 10)];
myBox.backgroundColor = [UIColor lightGrayColor];
[sender.view addSubview:myBox];
}
- (void)drawRectV2:(CGRect)rect{
UIBezierPath *circle = [UIBezierPath
bezierPathWithOvalInRect:CGRectMake(0, 0, 200, 200)];
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
[circle fill];
[circle stroke];
}
@end
如你所见,我有两种方法来做一些二维的事情。
而我的主要方法是这样的:
TwoDOperations *twoD = [[TwoDOperations alloc]init];
CGRect rect = CGRectMake(10,10,10,10);
[twoD drawRect:rect withscene:self];
[twoD drawRectV2:rect];
(我知道rect暂时不用)
当我使用<twoD drawRect:rect with scene:self>
时,它正在成功绘制。我可以画一个矩形。但是当我使用 <twoD drawRectV2:rect>
时,它什么都不是。什么都不画?我检查调试模式,发现上下文不为空。我只想问为什么?
因为你的drawRectV2:
方法真的什么都不做。您正在使用 UIGraphicsBeginImageContext(200, 200)
创建一个图像上下文并在此图形上下文中绘制一个圆圈,但您不使用此绘图的结果。创建图形上下文只是为您提供一些图形缓冲区(或者换句话说 - 图形表面),然后您绘制到这个 buffer/surface 中。但是你不会看到绘图的结果,除非你绘制到 iOS 视图绘图系统提供的系统上下文(我的意思是在被覆盖的 UIView
中调用时使用 UIGraphicsGetCurrentContext()
获得的上下文drawRect:
方法)。
要在您的代码中获取绘图结果,您需要调用 UIGraphicsGetImageFromCurrentImageContext()
函数,其中 return 是 UIImage
。除此之外,如果调用 UIGraphicsBeginImageContext()
函数,则必须调用 UIGraphicsEndImageContext()
。请参阅 "Drawing and Printing Guide for iOS", "Creating New Images Using Bitmap Graphics Contexts" section 使用我上面提到的功能的示例。
但是还有一个问题。假设您通过调用 UIGraphicsGetImageFromCurrentImageContext()
获得绘图结果(即 UIImage
);您将如何处理此绘图结果,即您将如何使用获得的 UIImage
?
一般来说,您对 TwoDOperations
class 的实施看起来很奇怪。看来您需要阅读更多文档才能理解 iOS 视图绘图的概念。我建议您阅读 "View Programming Guide for iOS", "Implementing Your Drawing Code" section.
据我了解您的意图,您想要创建一个视图来执行一些自定义绘图,例如一个圆圈。如果这种理解是正确的,那么我将按以下方式实现它:
--------TwoDOperations.h------
@interface TwoDOperations : UIView
// No need to declare anything as we're only going to override UIView's methods
@end
--------TwoDOperations.m------
#import "TwoDOperations.h"
@implementation TwoDOperations
- (instancetype)init
{
self = [super init];
if (self != nil) {
// Custom initialization
self.backgroundColor = [UIColor lightGrayColor];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:rect];
[circle fill];
[circle stroke];
CGContextRestoreGState(context);
}
@end
--------ViewController.m------
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect frame = CGRectMake(0, 0, 200, 200);
TwoDOperations* twoDOperationsView = [[TwoDOperations alloc] initWithFrame:frame];
[self.view addSubview:twoDOperationsView];
}
在上面的示例中,TwoDOperations
的 drawRect:
方法由 iOS 绘图系统自动调用,并且 UIGraphicsGetCurrentContext() 为您提供了正确的系统图形上下文。 iOS 绘图系统然后从该系统图形上下文中获取绘图结果并将其呈现在显示器上。你不应该自己调用 UIView
的绘图方法,只有系统应该这样做。例如,如果您在代码中手动调用 TwoDOperations
的 drawRect:
方法,那么 UIGraphicsGetCurrentContext() 很可能不会 return 正确的图形上下文,因此绘图不会完成.
更新
根据您对进度条的评论,我正在更新我的回答。
因此,通常您希望自定义视图根据某些外部条件绘制不同的内容。例如。默认情况下,视图应该绘制一个矩形(进度条背景)和一个较小的矩形来反映一些默认进度条的值;如果用户按下某个按钮,则进度条值会发生变化,并且应使用新尺寸重新绘制一个较小的矩形。在这种情况下,最好的方法是根据表示进度条值的数据在自定义视图的 drawRect:
方法中进行不同的绘制。您不需要有 2 个视图来表示您的进度条;您只需要在视图控制器的 viewDidLoad
方法中添加一次视图。然后,当用户按下一些按钮时,您可以更改自定义视图的数据,以便其 drawRect:
方法将绘制一个新的进度条。
我会按如下方式实现:
--------TwoDOperations.h------
@interface TwoDOperations : UIView
// Progress bar's value, percents (from 0.0 to 1.0)
@property (nonatomic, assign) CGFloat progressBarValue;
@end
--------TwoDOperations.m------
#import "TwoDOperations.h"
@implementation TwoDOperations
- (instancetype)init
{
self = [super init];
if (self != nil) {
// Custom initialization
self.backgroundColor = [UIColor lightGrayColor];
self.progressBarValue = 0.1f; // some default value
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
// Draw progress bar frame
CGContextStrokeRectWithWidth(context, rect, 2.0f);
CGFloat valueWidth = rect.size.width * self.progressBarValue;
CGRect valueRect = CGRectMake(rect.origin.x, rect.origin.y, valueWidth, rect.size.height);
CGContextFillRect(context, valueRect);
CGContextRestoreGState(context);
}
@end
--------ViewController.m------
#import "TwoDOperations.h"
// Declare your custom view as a property in your view controller's extension
@interface ViewController ()
@property (nonatomic, strong) TwoDOperations* progressBarView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect frame = CGRectMake(0, 0, 200, 200);
self.progressBarView = [[TwoDOperations alloc] initWithFrame:frame];
[self.view addSubview:self.progressBarView];
}
- (IBAction)buttonPressed:(UIButton*)sender
{
self.progressBarView.progressBarValue = 0.6f;
}
@end
我尝试编写自己的自定义 class 来做一些二维的东西。 (TwoDOperations.h 和 TwoDOperations.m)。以下是我的文件。
--------TwoDOperations.h------
@interface TwoDOperations : UIView
- (void)drawRect:(CGRect)rect withscene:(UIViewController *)sender;
- (void)drawRectV2:(CGRect)rect;
-(instancetype)init;
@end
--------TwoDOperations.m------
#import "TwoDOperations.h"
@implementation TwoDOperations
-(instancetype)init{
self=[super init];
if (self)
{
}
return self;
}
- (void)drawRect:(CGRect)rect withscene:(UIViewController *)sender{
UIView *myBox = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 300, 10)];
myBox.backgroundColor = [UIColor lightGrayColor];
[sender.view addSubview:myBox];
}
- (void)drawRectV2:(CGRect)rect{
UIBezierPath *circle = [UIBezierPath
bezierPathWithOvalInRect:CGRectMake(0, 0, 200, 200)];
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
[circle fill];
[circle stroke];
}
@end
如你所见,我有两种方法来做一些二维的事情。
而我的主要方法是这样的:
TwoDOperations *twoD = [[TwoDOperations alloc]init];
CGRect rect = CGRectMake(10,10,10,10);
[twoD drawRect:rect withscene:self];
[twoD drawRectV2:rect];
(我知道rect暂时不用)
当我使用<twoD drawRect:rect with scene:self>
时,它正在成功绘制。我可以画一个矩形。但是当我使用 <twoD drawRectV2:rect>
时,它什么都不是。什么都不画?我检查调试模式,发现上下文不为空。我只想问为什么?
因为你的drawRectV2:
方法真的什么都不做。您正在使用 UIGraphicsBeginImageContext(200, 200)
创建一个图像上下文并在此图形上下文中绘制一个圆圈,但您不使用此绘图的结果。创建图形上下文只是为您提供一些图形缓冲区(或者换句话说 - 图形表面),然后您绘制到这个 buffer/surface 中。但是你不会看到绘图的结果,除非你绘制到 iOS 视图绘图系统提供的系统上下文(我的意思是在被覆盖的 UIView
中调用时使用 UIGraphicsGetCurrentContext()
获得的上下文drawRect:
方法)。
要在您的代码中获取绘图结果,您需要调用 UIGraphicsGetImageFromCurrentImageContext()
函数,其中 return 是 UIImage
。除此之外,如果调用 UIGraphicsBeginImageContext()
函数,则必须调用 UIGraphicsEndImageContext()
。请参阅 "Drawing and Printing Guide for iOS", "Creating New Images Using Bitmap Graphics Contexts" section 使用我上面提到的功能的示例。
但是还有一个问题。假设您通过调用 UIGraphicsGetImageFromCurrentImageContext()
获得绘图结果(即 UIImage
);您将如何处理此绘图结果,即您将如何使用获得的 UIImage
?
一般来说,您对 TwoDOperations
class 的实施看起来很奇怪。看来您需要阅读更多文档才能理解 iOS 视图绘图的概念。我建议您阅读 "View Programming Guide for iOS", "Implementing Your Drawing Code" section.
据我了解您的意图,您想要创建一个视图来执行一些自定义绘图,例如一个圆圈。如果这种理解是正确的,那么我将按以下方式实现它:
--------TwoDOperations.h------
@interface TwoDOperations : UIView
// No need to declare anything as we're only going to override UIView's methods
@end
--------TwoDOperations.m------
#import "TwoDOperations.h"
@implementation TwoDOperations
- (instancetype)init
{
self = [super init];
if (self != nil) {
// Custom initialization
self.backgroundColor = [UIColor lightGrayColor];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:rect];
[circle fill];
[circle stroke];
CGContextRestoreGState(context);
}
@end
--------ViewController.m------
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect frame = CGRectMake(0, 0, 200, 200);
TwoDOperations* twoDOperationsView = [[TwoDOperations alloc] initWithFrame:frame];
[self.view addSubview:twoDOperationsView];
}
在上面的示例中,TwoDOperations
的 drawRect:
方法由 iOS 绘图系统自动调用,并且 UIGraphicsGetCurrentContext() 为您提供了正确的系统图形上下文。 iOS 绘图系统然后从该系统图形上下文中获取绘图结果并将其呈现在显示器上。你不应该自己调用 UIView
的绘图方法,只有系统应该这样做。例如,如果您在代码中手动调用 TwoDOperations
的 drawRect:
方法,那么 UIGraphicsGetCurrentContext() 很可能不会 return 正确的图形上下文,因此绘图不会完成.
更新
根据您对进度条的评论,我正在更新我的回答。
因此,通常您希望自定义视图根据某些外部条件绘制不同的内容。例如。默认情况下,视图应该绘制一个矩形(进度条背景)和一个较小的矩形来反映一些默认进度条的值;如果用户按下某个按钮,则进度条值会发生变化,并且应使用新尺寸重新绘制一个较小的矩形。在这种情况下,最好的方法是根据表示进度条值的数据在自定义视图的 drawRect:
方法中进行不同的绘制。您不需要有 2 个视图来表示您的进度条;您只需要在视图控制器的 viewDidLoad
方法中添加一次视图。然后,当用户按下一些按钮时,您可以更改自定义视图的数据,以便其 drawRect:
方法将绘制一个新的进度条。
我会按如下方式实现:
--------TwoDOperations.h------
@interface TwoDOperations : UIView
// Progress bar's value, percents (from 0.0 to 1.0)
@property (nonatomic, assign) CGFloat progressBarValue;
@end
--------TwoDOperations.m------
#import "TwoDOperations.h"
@implementation TwoDOperations
- (instancetype)init
{
self = [super init];
if (self != nil) {
// Custom initialization
self.backgroundColor = [UIColor lightGrayColor];
self.progressBarValue = 0.1f; // some default value
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
// Draw progress bar frame
CGContextStrokeRectWithWidth(context, rect, 2.0f);
CGFloat valueWidth = rect.size.width * self.progressBarValue;
CGRect valueRect = CGRectMake(rect.origin.x, rect.origin.y, valueWidth, rect.size.height);
CGContextFillRect(context, valueRect);
CGContextRestoreGState(context);
}
@end
--------ViewController.m------
#import "TwoDOperations.h"
// Declare your custom view as a property in your view controller's extension
@interface ViewController ()
@property (nonatomic, strong) TwoDOperations* progressBarView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect frame = CGRectMake(0, 0, 200, 200);
self.progressBarView = [[TwoDOperations alloc] initWithFrame:frame];
[self.view addSubview:self.progressBarView];
}
- (IBAction)buttonPressed:(UIButton*)sender
{
self.progressBarView.progressBarValue = 0.6f;
}
@end