Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios drawRect无效上下文_Ios_Objective C_Drawrect - Fatal编程技术网

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
    用于在给定的时间点渲染视图。如果还没有要渲染的内容,它应该立即返回。当数据可用时,您向视图提供进行渲染所需的数据,并启动
    设置需要显示

  • 因此,一种常见的模式是在视图子类中有一个属性,并为该属性调用设置setter
    setNeedsDisplay

  • 而不是启动异步请求并尝试在两秒钟(或任意时间)内使用数据,而是为
    downloadData
    提供一个完成处理程序块参数,在下载完成时调用该参数,并在下载和解析完成后立即触发更新。这避免了不必要的延迟(例如,如果您等待两秒,但在0.5秒内获取数据,为什么要等待更长的时间;如果您需要两秒,但在2.1秒内获取数据,则可能没有任何数据可显示)。在下载和解析完成后立即启动视图更新

  • 这个
    float*
    引用是一个局部变量,永远不会被填充。你的
    downloadData
    可能应该在前面提到的完成处理程序中返回必要的数据。坦率地说,指向C数组的指针的概念无论如何都不是你应该在Objective-C中使用的模式。如果你的网络响应真的是eturns只返回两个float,这是您应该传递给此视图的内容,而不是
    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
    

    //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