试图根据从服务器下拉的值在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_after
块时,提供给drawRect
的上下文不再存在,并且您异步调用的块中也没有上下文抚摸这些线条。drawRect
用于在给定的时间渲染视图。如果尚无要渲染的内容,则应立即返回。当数据可用时,可以为视图提供执行渲染和启动setNeedsDisplay
所需的数据。
因此,一种常见的模式是在您的视图子类中具有一个属性,并为您设置该属性的设置器,然后为您调用setNeedsDisplay
。
而不是发起异步请求并尝试在两秒钟(或任何任意时间)内使用数据,而是给downloadData
一个完成处理程序块参数,该参数将在下载完成后调用,并在下载和解析完成后立即触发更新。这样可以避免不必要的延迟(例如,如果您等待两秒钟,但要在0.5秒内获取数据,为什么要等待比所需时间更长的时间;如果要两秒钟,但要在2.1秒内获取数据,则可能会没有任何数据要显示)。完全在下载和解析完成后启动视图的更新。
此float *
引用是局部变量,永远不会填充。您的downloadData
可能应该在上述完成处理程序中返回必要的数据。坦白说,这种指向C数组的指针的概念并不是您应该在Objective-C中使用的模式。如果您的网络响应实际上仅返回两个浮点数,那么您应该将其传递给该视图,而不是float *
。
注意,我已经用UIKit图形替换了CoreGraphics代码。就个人而言,我倾向于走到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
和
// 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
注意,这不是在进行任何网络请求。它只是呈现提供给它的任何值。 values
的设置器将为我们触发setNeedsDisplay
。
然后
// 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
现在,让我来建立NSArray
值NSNumber
,这是一个完全独立的问题。而且,虽然将网络/解析代码从视图中移出并移入视图控制器会更好一些,但它甚至可能不属于该位置。您可能还有另一个对象专门用于执行网络请求和/或解析结果。但是,同样,这可能超出了此问题的范围。
但是希望这说明了这个想法:将视图从执行网络请求或解析数据的业务中脱颖而出。是否只渲染提供的任何数据。产生: