Пара наблюдений:
Этот недопустимый контекст является результатом того, что вы инициируете асинхронный процесс, поэтому к тому времени, когда вызывается блок dispatch_after
, предоставляется контекст на drawRect
больше не существует, и у вашего асинхронно вызываемого блока нет контекста, по которому можно перемещаться по этим строкам.
Но представление не должно инициировать этот сетевой запрос и анализ. Обычно контроллер представления (или, лучше, другой сетевой контроллер и т. П.) Должен инициировать сетевой запрос и анализ.
drawRect
предназначен для визуализации представления при заданном момент времени. Если еще нечего рендерить, он должен сразу вернуться. Когда данные доступны, вы предоставляете представлению данные, необходимые для рендеринга, и запускаете setNeedsDisplay
.
Таким образом, общий шаблон будет иметь свойство в вашем подклассе представления и иметь установщик для этого свойства, вызывая для вас setNeedsDisplay
.
Вместо того, чтобы инициировать асинхронный запрос и пытаться использовать данные в течение двух секунд (или любого произвольного промежутка времени), вы вместо этого задаете downloadData
параметр блока обработчика завершения, который он вызывает после завершения загрузки, и сразу же запустить обновление, как только загрузка и анализ будут завершены. Это позволяет избежать ненужных задержек (например, если вы ждете две секунды, но получаете данные за 0,5 секунды, зачем ждать дольше, чем необходимо; если вам нужно две секунды, но получите данные за 2,1 секунды, вы рискуете не иметь никаких данных для отображения). Инициируйте обновление представления точно после завершения загрузки и анализа.
Эта ссылка float *
является локальной переменной и никогда не будет заполнена. Ваш downloadData
, вероятно, должен вернуть необходимые данные в вышеупомянутом обработчике завершения. Честно говоря, это понятие указателя на массив C не является шаблоном, который вы должны использовать в Objective- C, в любом случае. Если ваш сетевой ответ действительно возвращает только два числа с плавающей точкой, это то, что вы должны передать этому представлению, а не float *
.
Обратите внимание, я заменил код CoreGraphics на рисунок UIKit , Лично я был бы склонен к go дальше и перешел бы к CAShapeLayer
и вообще не имел бы drawRect
. Но я не хотел бросать в тебя слишком много. Но общая идея заключается в том, чтобы использовать наивысший уровень абстракции, насколько это возможно, и нет необходимости разбираться в сорняках CoreGraphics для чего-то столь же простого, как это.
Это будет не совсем правильно, так как я не совсем понимаю, каковы данные вашей модели, но давайте на секунду предположим, что это просто возвращает серию float ценности. Таким образом, у вас может быть что-то вроде:
// 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
значений, так как это совершенно отдельный вопрос. И хотя перемещение этой сети / кода разбора из вида в контроллер представления немного лучше, он, вероятно, даже не принадлежит. У вас может быть другой объект, предназначенный для выполнения сетевых запросов и / или анализа результатов. Но, опять же, это, вероятно, выходит за рамки этого вопроса.
Но, надеюсь, это иллюстрирует идею: получить представление о бизнесе по выполнению сетевых запросов или анализу данных. Пусть он просто отобразит все предоставленные данные.
Это дает: