Ответ 1
НЕ ИСПОЛЬЗУЙТЕ 0,5 в качестве ширины.
замените 0.5
на 1.0 / [UIScreen mainScreen].scale
Когда вы нарисуете слой, вы можете получить линию ширины 1 пикселя.
Я хочу нарисовать двойную цветную линию с линиями 1px из двух разных цветов. У меня много кода, поэтому я пытаюсь объяснить его.
Если я использую метод uiview drawRect
, он отлично работает, когда я выключаю сглаживание, но когда я использую слой drawLayerInContext
, он либо отображает одну простую линию, либо две простые линии 2px с сглаживанием или двумя полупрозрачными линиями 2px с антилечением.
Мне удалось получить аналогичное поведение, чем метод UIView drawRect, создав изображение с настраиваемым контекстом, где я могу указать масштаб:
UIGraphicsBeginImageContextWithOptions([self bounds].size, NO, 0.f); // 0.f for scale means "scale for device main screen".
Я просто хотел бы знать, почему я не получаю такое же поведение в drawInContext
, и если бы был способ получить подобное поведение.
Вот код, который рисует двухцветную строку:
void DRAW_DOUBLE_LINE(CGContextRef ctx, CGPoint startPoint, CGPoint endPoint, UIColor* topColor, UIColor* bottomColor)
{
UIGraphicsPushContext(ctx);
UIBezierPath *topLine = [[UIBezierPath alloc] init];
CGPoint topLineStartPoint = startPoint;
CGPoint topLineEndPoint = endPoint;
[topLine moveToPoint:topLineStartPoint];
[topLine addLineToPoint:topLineEndPoint];
[topColor setStroke];
topLine.lineWidth = 0.5;
[topLine stroke];
UIBezierPath *bottomLine = [[UIBezierPath alloc] init];
CGPoint bottomLineStartPoint = topLineStartPoint;
bottomLineStartPoint.y +=0.5;
CGPoint bottomLineEndPoint = topLineEndPoint;
bottomLineEndPoint.y +=0.5;
[bottomLine moveToPoint:bottomLineStartPoint];
[bottomLine addLineToPoint:bottomLineEndPoint];
[bottomColor setStroke];
bottomLine.lineWidth = 0.5;
[bottomLine stroke];
UIGraphicsPopContext();
}
С помощью метода drawRect
UIView
я получаю следующее:
| Points y coordinate | Antialiasing | Result | | ------------------- | ------------ | ------------------------------- | | 5 | NO | 2 lines of 1 px: Bingo! | | 5.25 | NO | 2 lines of 1 px: Bingo! | | 5.5 | NO | 2 lines of 1 px: Bingo! | | 5 | YES | 3 half transparent lines of 1px | | 5.25 | YES | 2 lines of 1 px: Bingo! | | 5.5 | YES | 3 half transparent lines of 1px |
В CALayer с drawInContext я получаю эти результаты
| Points y coordinate | Antialiasing | Result | | ------------------- | ------------ | ------------------------------- | | 5 | NO | 2 lines of 2 px | | 5.25 | NO | 1 line of 2 px | | 5.5 | NO | 1 line of 2 px | | 5 | YES | 2 half transparent lines of 2px | | 5.25 | YES | 1 half transparent line of 2px | | 5.5 | YES | 2 half transparent lines of 2px |
Используя мой пользовательский контекст, я получаю следующее:
| Points y coordinate | Antialiasing | Result | | ------------------- | ------------ | ------------------------------- | | 5 | NO | 2 lines of 1 px: Bingo! | | 5.25 | NO | 2 lines of 1 px: Bingo! | | 5.5 | NO | 2 lines of 1 px: Bingo! | | 5 | YES | 3 half transparent lines of 1px | | 5.25 | YES | 2 lines of 1 px: Bingo! | | 5.5 | YES | 3 half transparent lines of 1px |
Код для drawRect
реализации:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint startPoint = CGPointMake(0, 5);
CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds),5);
UIColor* topLineColor = [UIColor whiteColor];
UIColor* bottomLineColor = [UIColor blackColor];
DRAW_DOUBLE_LINE(context, startPoint, endPoint, topLineColor, bottomLineColor);
}
Код для drawInContext
реализации:
-(void)drawInContext:(CGContextRef)ctx{
CGPoint startPoint = CGPointMake(0, 5);
CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds),5);
UIColor* topLineColor = [UIColor whiteColor];
UIColor* bottomLineColor = [UIColor blackColor];
DRAW_DOUBLE_LINE(ctx, startPoint, endPoint, topLineColor, bottomLineColor);
}
Код для реализации пользовательского контекста в методе CALayer display
:
-(void)display{
if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
UIGraphicsBeginImageContextWithOptions([self bounds].size, NO, 0.f); // 0.f for scale means "scale for device main screen".
} else {
UIGraphicsBeginImageContext([self bounds].size);
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint startPoint = CGPointMake(0, 5.25);
CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds),5.25);
UIColor* topLineColor = [UIColor whiteColor];
UIColor* bottomLineColor = [UIColor blackColor];
DRAW_DOUBLE_LINE(ctx, startPoint, endPoint, topLineColor, bottomLineColor);
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.contents = (id)coloredImg.CGImage;
}
НЕ ИСПОЛЬЗУЙТЕ 0,5 в качестве ширины.
замените 0.5
на 1.0 / [UIScreen mainScreen].scale
Когда вы нарисуете слой, вы можете получить линию ширины 1 пикселя.
Я бы предложил вместо рисования строк просто нарисовать заполненные прямоугольники нужного размера. Одной из сложнейших частей с разными разрешениями являются местоположения начальной и конечной точек линий. Но рисовать прямоугольники намного проще.
Координаты для прямоугольников всегда четные, поскольку вы нацеливаете край, а не центр пикселя.
Нарисуйте и заполните прямоугольники вместо строк. Координаты для прямоугольников всегда должны быть на краю пикселя, а не центра.
Лучший способ, который я нашел, чтобы получить четкие/четкие линии 1px на сетчатке/не-сетчатке и поддерживать их как 1px, даже если вы увеличиваете масштаб, это писать шейдер. В моем случае я использовал OpenGL, но я считаю, что вы можете сделать это с помощью Metal for CALayers. Основной процесс состоит в том, чтобы нарисовать прямоугольник нулевой высоты (или нулевую ширину, если линия вертикальна), а затем в шейдере надавите эти точки наружу на нужное количество пикселей.
У меня появилась идея отсюда: https://www.mapbox.com/blog/drawing-antialiased-lines/