Ios drawRect无效上下文
试图从服务器下拉的值在Ios drawRect无效上下文,ios,objective-c,drawrect,Ios,Objective C,Drawrect,试图从服务器下拉的值在UIView中绘制图形 我有一个块成功地拉取了开始/结束点(我必须添加延迟,以确保数组在开始之前具有值。我尝试在调度内部和外部移动CGContextRef,但仍然得到“无效上下文” 我尝试过在不同的地方添加[self-setNeedsDisplay];,但运气不好 代码如下: - (void)drawRect:(CGRect)rect { // Drawing code // Array - accepts values from method
UIView
中绘制图形
我有一个块成功地拉取了开始/结束点(我必须添加延迟,以确保数组在开始之前具有值。我尝试在调度内部和外部移动CGContextRef
,但仍然得到“无效上下文”
我尝试过在不同的地方添加[self-setNeedsDisplay];
,但运气不好
代码如下:
- (void)drawRect:(CGRect)rect {
// Drawing code
// Array - accepts values from method
float *values;
UIColor * greenColor = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0];
UIColor * redColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0];
// Call to method to run server query, get data, parse (TBXML), assign values to array
// this is working - NSLog output shows proper values are downloaded and parsed...
values = [self downloadData];
// Get context
CGContextRef context = UIGraphicsGetCurrentContext();
NSLog (@"Context: %@", context);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"Waiting for array to populate from URL/Parsing....");
NSLog(@"length 1: %f", values[0]);
NSLog(@"length 2: %f", values[1]);
float starty = 100.0;
float startleft = 25.0;
CGContextSetLineWidth (context, 24.0);
CGContextSetStrokeColorWithColor (context, greenColor.CGColor);
CGContextMoveToPoint(context, startleft, starty);
CGContextAddLineToPoint(context, values[0], starty);
NSLog(@"Start/Stop Win values: %f", values[0]);
CGContextStrokePath (context);
starty = starty + 24.0;
CGContextSetLineWidth (context, 24.0);
CGContextSetStrokeColorWithColor (context, redColor.CGColor);
CGContextMoveToPoint(context, startleft, starty);
CGContextAddLineToPoint(context, values[1], starty);
NSLog(@"Start/Stop Loss values: %f", values[1]);
CGContextStrokePath (context);
*/
});
}
以下是几点观察:
dispatch\u after
块时,提供给drawRect
的上下文不再存在,并且异步调用的块没有可在其中划行的上下文
但是视图不应该启动这个网络请求和解析。通常视图控制器(或者更好,其他一些网络控制器或类似的)应该启动网络请求和解析
drawRect
用于在给定的时间点渲染视图。如果还没有要渲染的内容,它应该立即返回。当数据可用时,您向视图提供进行渲染所需的数据,并启动设置需要显示
setNeedsDisplay
downloadData
提供一个完成处理程序块参数,在下载完成时调用该参数,并在下载和解析完成后立即触发更新。这避免了不必要的延迟(例如,如果您等待两秒,但在0.5秒内获取数据,为什么要等待更长的时间;如果您需要两秒,但在2.1秒内获取数据,则可能没有任何数据可显示)。在下载和解析完成后立即启动视图更新
float*
引用是一个局部变量,永远不会被填充。你的downloadData
可能应该在前面提到的完成处理程序中返回必要的数据。坦率地说,指向C数组的指针的概念无论如何都不是你应该在Objective-C中使用的模式。如果你的网络响应真的是eturns只返回两个float,这是您应该传递给此视图的内容,而不是float*
CAShapeLayer
,而根本没有drawRect
。但我不想对您施加太多的压力。但总体思路是尽可能使用最高级别的抽象,不需要我为了像这样简单的事情,我在CoreGraphics的杂草中找到了答案
这可能不太正确,因为我不太了解模型数据是什么,但让我们假设它只是返回一系列浮点值。因此,您可能会有如下情况:
// BarView.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface BarView : UIView
@property (nonatomic, copy, nullable) NSArray <NSNumber *> *values;
@end
NS_ASSUME_NONNULL_END
及
//ViewController.m
#导入“ViewController.h”
#导入“BarView.h”
@界面视图控制器()
@属性(非原子,弱)IBOutlet BarView*BarView;
@结束
@实现视图控制器
-(无效)viewDidLoad{
[超级视图下载];
[自行下载:^(NSArray*值,NSError*错误){
如果(错误){
NSLog(@“%@”,错误);
返回;
}
self.barView.values=值;
}];
}
-下载:(void(^)(NSArray*,NSError*)完成{
NSURL*url=[NSURL URLWithString:@“…”;
[[[NSURLSession sharedSession]dataTaskWithURL:url completionHandler:^(NSData*\u可为空的数据,NSURLResponse*\u可为空的响应,NSError*\u可为空的错误){
//在这里解析数据
如果(错误){
dispatch\u async(dispatch\u get\u main\u queue()^{
完成(无,错误);
});
返回;
}
NSArray*值=。。。
//完成后,调用完成处理程序
dispatch\u async(dispatch\u get\u main\u queue()^{
完成(价值,无);
});
}]恢复];
}
@结束
现在,我将让您自行构建NSNumber
值的NSArray
,因为这是一个完全独立的问题。将此网络/解析代码移出视图并移入视图控制器会更好一些,但它可能甚至不属于那里。您可能有另一个专用于执行n的对象网络请求和/或解析结果。但是,同样,这可能超出了这个问题的范围
但希望这说明了这个想法:让视图脱离执行网络请求或解析数据的业务,让它只呈现提供的任何数据
这将产生:
有几个观察结果:
dispatch\u after
块时,提供给drawRect
的上下文不再存在,并且异步调用的块没有可在其中划行的上下文
但是视图不应该启动这个网络请求和解析。通常视图控制器(或者更好,其他一些网络控制器或类似的)应该启动网络请求和解析
drawRect
用于在给定的时间点渲染视图。如果还没有要渲染的内容,它应该立即返回。当数据可用时,您向视图提供进行渲染所需的数据,并启动设置需要显示
// BarView.m
#import "BarView.h"
@implementation BarView
- (void)drawRect:(CGRect)rect {
if (!self.values) { return; }
NSArray *colors = @[UIColor.greenColor, UIColor.redColor]; // we’re just alternating between red and green, but do whatever you want
float y = 100.0;
float x = 25.0;
for (NSInteger i = 0; i < self.values.count; i++) {
float value = [self.values[i] floatValue];
UIBezierPath *path = [UIBezierPath bezierPath];
path.lineWidth = 24;
[colors[i % colors.count] setStroke];
[path moveToPoint:CGPointMake(x, y)];
[path addLineToPoint:CGPointMake(x + value, y)];
[path stroke];
y += 24;
}
}
- (void)setValues:(NSArray<NSNumber *> *)values {
_values = [values copy];
[self setNeedsDisplay];
}
@end
// ViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ViewController : UIViewController
- (void)download:(void (^)(NSArray <NSNumber *> * _Nullable, NSError * _Nullable))completion;
@end
NS_ASSUME_NONNULL_END
// ViewController.m
#import "ViewController.h"
#import "BarView.h"
@interface ViewController ()
@property (nonatomic, weak) IBOutlet BarView *barView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self download:^(NSArray <NSNumber *> *values, NSError *error) {
if (error) {
NSLog(@"%@", error);
return;
}
self.barView.values = values;
}];
}
- (void)download:(void (^)(NSArray <NSNumber *> *, NSError *))completion {
NSURL *url = [NSURL URLWithString:@"..."];
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// parse the data here
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, error);
});
return;
}
NSArray *values = ...
// when done, call the completion handler
dispatch_async(dispatch_get_main_queue(), ^{
completion(values, nil);
});
}] resume];
}
@end