Reputation: 83
I need to draw a curve with a translucent color. But if the curve contains many points, then you can get a strange effect. How to get rid of it?
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 ];
} ];
Upvotes: 0
Views: 51
Reputation: 77452
Curious...
From experience, UIBezierPath
can be rather quirky. In this case, I'm guessing this particular quirk is caused by internal optimizations.
The reason I believe that to be the case is because if we generate a path with 256 points -- 1 moveTo + 255 addLineTo commands -- we don't see the problem:
However, as soon as we use 1 moveTo + 256 addLineTo commands -- for a total of 257 points, we get this:
One option to get around that would be to render a temporary image with clear background and 100% opaque stroke, and then render that image with 50% alpha:
// 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];
} ];
Here's a complete, runnable example -- starts with 250 addLineTo commands... each tap anywhere increments that by 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
Looks like this after 7 taps:
Upvotes: 2