UIBezierPath 中风与 alpha 问题/伪影

问题描述 投票:0回答:1

我需要绘制一条半透明颜色的曲线。但如果曲线包含很多点,那么你会得到奇怪的效果。如何摆脱它?

UIBezierPath * bezierPath = [ UIBezierPath bezierPath ];
bezierPath.lineCapStyle = kCGLineCapRound;
bezierPath.lineJoinStyle = kCGLineJoinRound;
bezierPath.lineWidth = 80.0;
[ bezierPath moveToPoint:CGPointMake(50.0, 50.0) ];
for (int i = 0; i < 900; i++)
{
    [ bezierPath addLineToPoint:CGPointMake(50.0 + i, 50.0) ];
}

UIGraphicsImageRendererFormat * format = [ UIGraphicsImageRendererFormat defaultFormat ];
format.scale = 1.0;
format.opaque = NO;
format.preferredRange = UIGraphicsImageRendererFormatRangeStandard;
CGSize size = CGSizeMake(1000.0, 100.0);
UIGraphicsImageRenderer * renderer = [ [ UIGraphicsImageRenderer alloc ] initWithSize:size format:format ];
UIImage * image = [ renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext)
{
    [ [ UIColor clearColor ] setFill ];
    [ [ UIColor colorWithWhite:0.0 alpha:0.5 ] setStroke ];
    [ bezierPath stroke ];
} ];

ios objective-c uibezierpath cgcontext uigraphicsimagerenderer
1个回答
0
投票

好奇...

根据经验,

UIBezierPath
可能相当古怪。在这种情况下,我猜测这个特殊的怪癖是由内部优化引起的。

我之所以相信这种情况,是因为如果我们生成一条包含 256 个点的路径——1 个 moveTo + 255 个 addLineTo 命令——我们就看不到问题:

enter image description here

但是,一旦我们使用 1 moveTo + 256 addLineTo 命令——总共 257 点,我们就会得到:

enter image description here

解决这个问题的一个选择是渲染一个具有清晰背景和 100% 不透明描边的临时图像,然后使用 50% Alpha 渲染that图像:

// create image with solid stroke
UIImage * tmpImage = [ renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext)
{
    [ [ UIColor clearColor ] setFill ];
    [ [ UIColor colorWithWhite:0.0 alpha:1.0 ] setStroke ];
    [ bezierPath stroke ];
} ];

// create image2 by rendering tmpImage at 50% alpha
UIImage * image2 = [ renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext)
{
    [tmpImage drawAtPoint:CGPointZero blendMode:kCGBlendModeNormal alpha:0.5];
} ];

这是一个完整的、可运行的示例——以 250 个 addLineTo 命令开始...每次点击任意位置都会使该命令增加 1:

#import <UIKit/UIKit.h>

@interface AlphaPathVC : UIViewController

@end

@interface AlphaPathVC ()
{
    NSInteger nPoints;
    UIImageView *imgView1;
    UIImageView *imgView2;
    UILabel *infoLabel;
}
@end

@implementation AlphaPathVC

- (void)viewDidLoad {
    
    [super viewDidLoad];
    self.view.backgroundColor = UIColor.systemYellowColor;
    
    imgView1 = [UIImageView new];
    imgView2 = [UIImageView new];
    
    imgView1.backgroundColor = UIColor.whiteColor;
    imgView2.backgroundColor = UIColor.whiteColor;

    infoLabel = [UILabel new];
    infoLabel.backgroundColor = UIColor.whiteColor;
    infoLabel.numberOfLines = 0;
    infoLabel.text = @"Tap anywhere to increase number of points and re-generate images...";
    
    [self.view addSubview:imgView1];
    [self.view addSubview:imgView2];
    [self.view addSubview:infoLabel];
    
    nPoints = 250;
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    
    UIEdgeInsets i = self.view.safeAreaInsets;
    imgView1.frame = CGRectMake(i.left + 20.0, i.top + 20.0        , 1000.0, 100.0);
    imgView2.frame = CGRectMake(i.left + 20.0, i.top + 20.0 + 120.0, 1000.0, 100.0);
    
    infoLabel.frame = CGRectMake(i.left + 20.0, i.top + 20.0 + 240.0, 200.0, 80.0);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    UIBezierPath * bezierPath = [ UIBezierPath new ];
    bezierPath.lineCapStyle = kCGLineCapRound;
    bezierPath.lineJoinStyle = kCGLineJoinRound;
    bezierPath.lineWidth = 80.0;
    
    [ bezierPath moveToPoint:CGPointMake(50.0, 50.0) ];

    infoLabel.text = [NSString stringWithFormat:@" moveTo plus\n %ld addLineTo", (long)nPoints];
    
    for (int i = 0; i < nPoints; i++)
    {
        [ bezierPath addLineToPoint:CGPointMake(50.0 + i, 50.0) ];
    }
    
    UIGraphicsImageRendererFormat * format = [ UIGraphicsImageRendererFormat defaultFormat ];
    format.scale = 1.0;
    format.opaque = NO;
    format.preferredRange = UIGraphicsImageRendererFormatRangeStandard;
    CGSize size = CGSizeMake(1000.0, 100.0);
    UIGraphicsImageRenderer * renderer = [ [ UIGraphicsImageRenderer alloc ] initWithSize:size format:format ];
    
    // create image with alpha stroke
    UIImage * image1 = [ renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext)
    {
        [ [ UIColor clearColor ] setFill ];
        [ [ UIColor colorWithWhite:0.0 alpha:0.5 ] setStroke ];
        [ bezierPath stroke ];
    } ];

    // create image with solid stroke
    UIImage * tmpImage = [ renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext)
    {
        [ [ UIColor clearColor ] setFill ];
        [ [ UIColor colorWithWhite:0.0 alpha:1.0 ] setStroke ];
        [ bezierPath stroke ];
    } ];
    
    // create image2 by rendering tmpImage at 50% alpha
    UIImage * image2 = [ renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext)
    {
        [tmpImage drawAtPoint:CGPointZero blendMode:kCGBlendModeNormal alpha:0.5];
    } ];
    
    imgView1.image = image1;
    imgView2.image = image2;

    ++nPoints;
    
}

@end

点击 7 次后看起来像这样:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.