UIView с пунктирной линией
Что у меня:
![enter image description here]()
Чтобы создать эту строку, у меня в основном есть UIView
, и я делаю следующее:
void setLayerToLineFromAToB(CALayer *layer, CGPoint a, CGPoint b, CGFloat lineWidth)
{
CGPoint center = { 0.5 * (a.x + b.x), 0.5 * (a.y + b.y) };
CGFloat length = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
CGFloat angle = atan2(a.y - b.y, a.x - b.x);
layer.position = center;
layer.bounds = (CGRect) { {0, 0}, { length + lineWidth, lineWidth } };
layer.transform = CATransform3DMakeRotation(angle, 0, 0, 1);
}
Примечание. Этот код был найден здесь в stackoverflow, поэтому, если кто-то может дать мне ссылку на него, я был бы признателен.
Что я хочу:
![enter image description here]()
Итак, мне нужна только одна вещь - создать этот шаблон на UIView
. Я знаю, что могу сделать это с помощью Quartz2D (простой способ сделать это можно найти здесь). Но я хочу сделать это, манипулируя CALayer
и не прибегая к методу draw. Зачем? Из-за преобразования, которое я делаю на моем UIView
, я не могу правильно рисовать, используя метод draw
.
Изменить 1:
Просто для иллюстрации моей проблемы:
![enter image description here]()
Обычно у вас есть UIView
, а затем вы в основном просто рисуете что-то в нем (в данном случае простая строка). Решение, которое я нашел, чтобы избавиться от "серой" области, состояло в том, чтобы вместо чего-то рисовать, просто преобразуйте сам UIView
. Он работает хорошо, если вы хотите полностью заполненную линию, проблема возникает, когда вы хотите пунктир.
Ответы
Ответ 1
Примечание. Код от принца действительно помог мне, поэтому я дам ему +10 для подсказок. Но, в конце концов, я добавляю свой код. Я также добавлю в него некоторый контекст, поэтому он может быть полезен будущим читателям.
Последний код был таким:
-(void)updateLine{
// Important, otherwise we will be adding multiple sub layers
if ([[[self layer] sublayers] objectAtIndex:0])
{
self.layer.sublayers = nil;
}
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setBounds:self.bounds];
[shapeLayer setPosition:self.center];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
[shapeLayer setLineWidth:3.0f];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setLineDashPattern:
[NSArray arrayWithObjects:[NSNumber numberWithInt:10],
[NSNumber numberWithInt:5],nil]];
// Setup the path
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, beginPoint.center.x, beginPoint.center.y);
CGPathAddLineToPoint(path, NULL, endPoint.center.x, endPoint.center.y);
[shapeLayer setPath:path];
CGPathRelease(path);
[[self layer] addSublayer:shapeLayer];
}
В моем случае, startPoint и endPoint могут быть перемещены пользователем, используя KVO. Поэтому, когда один из них перемещается:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqual:@"position"])
{
[self updateLine];
}
}
Я много играл с принт-кодом. Я попытался использовать метод draw:
, который добавляет тонкую линию между пунктирной линией (немного странно...), и я также попытался использовать initWithFrame:
. Сам по себе его код без каких-либо модификаций дал бы мне такие ошибки на консоли:
<Error>: CGContextSaveGState: invalid context 0x0
<Error>: CGContextSetLineWidth: invalid context 0x0
<Error>: CGContextSetLineJoin: invalid context 0x0
<Error>: CGContextSetLineCap: invalid context 0x0
<Error>: CGContextSetMiterLimit: invalid context 0x0
<Error>: CGContextSetFlatness: invalid context 0x0
<Error>: CGContextAddPath: invalid context 0x0
<Error>: CGContextDrawPath: invalid context 0x0
<Error>: CGContextRestoreGState: invalid context 0x0
Ответ 2
Проверьте UIBezierPath setLineDash:count:phase:
method:
- (void)setLineDash:(const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase' method.
Это позволяет рисовать пунктирные линии.
- Сначала добавьте
CAShapeLayer
. Добавьте его в качестве подслоя в свой UIView
. У него есть свойство path
. - Теперь создайте объект
UIBezierPath
. Нарисуйте линию, используя setLineDash
.
Например:
UIBezierPath *path = [UIBezierPath bezierPath];
//draw a line
[path moveToPoint:yourStartPoint]; //add yourStartPoint here
[path addLineToPoint:yourEndPoint];// add yourEndPoint here
[path stroke];
CGFloat dashPattern[] = {2.0f,6.0f,4.0f,2.0f}; //make your pattern here
[path setLineDash:dashPattern count:4 phase:3];
UIColor *fill = [UIColor blueColor];
shapelayer.strokeStart = 0.0;
shapelayer.strokeColor = fill.CGColor;
shapelayer.lineWidth = 5.0;
shapelayer.lineJoin = kCALineJoinMiter;
shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:10],[NSNumber numberWithInt:7], nil];
shapelayer.lineDashPhase = 3.0f;
shapelayer.path = path.CGPath;
Примечание: этот ответ дает подсказку, чтобы вы могли импровизировать в соответствии с вашими требованиями.
Ответ 3
Swift 2.2
отбрасывая это здесь, чтобы сэкономить время.
extension UIView {
func addDashedLine(color: UIColor = UIColor.lightGrayColor()) {
layer.sublayers?.filter({ $0.name == "DashedTopLine" }).map({ $0.removeFromSuperlayer() })
self.backgroundColor = UIColor.clearColor()
let cgColor = color.CGColor
let shapeLayer: CAShapeLayer = CAShapeLayer()
let frameSize = self.frame.size
let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
shapeLayer.name = "DashedTopLine"
shapeLayer.bounds = shapeRect
shapeLayer.position = CGPoint(x: frameSize.width / 2, y: frameSize.height / 2)
shapeLayer.fillColor = UIColor.clearColor().CGColor
shapeLayer.strokeColor = cgColor
shapeLayer.lineWidth = 1
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineDashPattern = [4, 4]
let path: CGMutablePathRef = CGPathCreateMutable()
CGPathMoveToPoint(path, nil, 0, 0)
CGPathAddLineToPoint(path, nil, self.frame.width, 0)
shapeLayer.path = path
self.layer.addSublayer(shapeLayer)
}
}
Ответ 4
Вот версия Swift 3 Alexandre G ответ fooobar.com/questions/273909/...
extension UIView {
func addDashedLine(color: UIColor = .lightGray) {
layer.sublayers?.filter({ $0.name == "DashedTopLine" }).map({ $0.removeFromSuperlayer() })
backgroundColor = .clear
let shapeLayer = CAShapeLayer()
shapeLayer.name = "DashedTopLine"
shapeLayer.bounds = bounds
shapeLayer.position = CGPoint(x: frame.width / 2, y: frame.height / 2)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = color.cgColor
shapeLayer.lineWidth = 1
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineDashPattern = [4, 4]
let path = CGMutablePath()
path.move(to: CGPoint.zero)
path.addLine(to: CGPoint(x: frame.width, y: 0))
shapeLayer.path = path
layer.addSublayer(shapeLayer)
}
}
Ответ 5
Штриховая линия в Swift4 • Xcode 9
Создайте CAShapeLayer и используйте lineDashPattern
extension UIView {
func addDashedBorder() {
//Create a CAShapeLayer
let shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 2
// passing an array with the values [2,3] sets a dash pattern that alternates between a 2-user-space-unit-long painted segment and a 3-user-space-unit-long unpainted segment
shapeLayer.lineDashPattern = [2,3]
let path = CGMutablePath()
path.addLines(between: [CGPoint(x: 0, y: 0),
CGPoint(x: self.frame.width, y: 0)])
shapeLayer.path = path
layer.addSublayer(shapeLayer)
}
}
Использование:
dashView.addDashedBorder()
Выход:
![enter image description here]()
Ответ 6
Принятый ответ имеет проблему с координатами. Линия будет нарисована на некотором расстоянии ниже. И я не могу понять, почему и сколько расстояния увеличивается по координате Y.
Есть способ рисовать пунктирную линию с правильной координатой:
-(void)drawRect:(CGRect)rect
{
CGContextBeginPath(cx);
CGContextRef cx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(cx, _thickness);
CGContextSetStrokeColorWithColor(cx, _color.CGColor);
CGFloat dash[] = {_dashedLength,_dashedGap};
CGContextSetLineDash(cx, 0, dash, 2); // nb "2" == ra count
// CGContextSetLineCap(cx, kCGLineCapRound);
CGContextMoveToPoint(cx, 0, _thickness);
CGContextAddLineToPoint(cx, self.bounds.size.width, _thickness);
CGContextStrokePath(cx);
CGContextClosePath(cx);
}
Этот ответ получен из строки с пунктирной (не пунктирной!), с IBDesignable в 2017 году.
НЕ ЗАБРАВАЙТЕ НЕ ЗАПРЕЩАЕТСЯ установить цвет фона как белый, когда вы хотите черную пунктирную линию! По умолчанию вид имеет черный цвет фона, а цвет линии также черный, поэтому я думал, что это сплошная линия. Мне стоило полдня, чтобы узнать. Т_Т
Ответ 7
Прежде всего, кредит принадлежит RuiAAPeres и Prince, я просто инкапсулирую их ответы в объект UIView, который другие могут зайти в свои проекты и использовать
#import <UIKit/UIKit.h>
/**
* Simple UIView for a dotted line
*/
@interface H3DottedLine : UIView
/**
* Set the line thickness
*/
@property (nonatomic, assign) CGFloat thickness;
/**
* Set the line color
*/
@property (nonatomic, copy) UIColor *color;
/**
* Set the length of the dash
*/
@property (nonatomic, assign) CGFloat dashedLength;
/**
* Set the gap between dashes
*/
@property (nonatomic, assign) CGFloat dashedGap;
@end
@implementation H3DottedLine
#pragma mark - Object Lifecycle
- (instancetype)init {
self = [super init];
if (self) {
// Set Default Values
_thickness = 1.0f;
_color = [UIColor whiteColor];
_dashedGap = 1.0f;
_dashedLength = 5.0f;
}
return self;
}
#pragma mark - View Lifecycle
- (void)layoutSubviews {
// Note, this object draws a straight line. If you wanted the line at an angle you simply need to adjust the start and/or end point here.
[self updateLineStartingAt:self.frame.origin andEndPoint:CGPointMake(self.frame.origin.x+self.frame.size.width, self.frame.origin.y)];
}
#pragma mark - Setters
- (void)setThickness:(CGFloat)thickness {
_thickness = thickness;
[self setNeedsLayout];
}
- (void)setColor:(UIColor *)color {
_color = [color copy];
[self setNeedsLayout];
}
- (void)setDashedGap:(CGFloat)dashedGap {
_dashedGap = dashedGap;
[self setNeedsLayout];
}
- (void)setDashedLength:(CGFloat)dashedLength {
_dashedLength = dashedLength;
[self setNeedsLayout];
}
#pragma mark - Draw Methods
-(void)updateLineStartingAt:(CGPoint)beginPoint andEndPoint:(CGPoint)endPoint {
// Important, otherwise we will be adding multiple sub layers
if ([[[self layer] sublayers] objectAtIndex:0]) {
self.layer.sublayers = nil;
}
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setBounds:self.bounds];
[shapeLayer setPosition:self.center];
[shapeLayer setFillColor:[UIColor clearColor].CGColor];
[shapeLayer setStrokeColor:self.color.CGColor];
[shapeLayer setLineWidth:self.thickness];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setLineDashPattern:@[@(self.dashedLength), @(self.dashedGap)]];
// Setup the path
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, beginPoint.x, beginPoint.y);
CGPathAddLineToPoint(path, NULL, endPoint.x, endPoint.y);
[shapeLayer setPath:path];
CGPathRelease(path);
[[self layer] addSublayer:shapeLayer];
}
@end
Ответ 8
Обновите Swift 5 и UIBezierPath
Для тех, кто работает с UIBezierPath
вместо CAShapeLayer
, вот как это сделать
class MyView: UIView {
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
// >> define the pattern & apply it
let dashPattern: [CGFloat] = [4.0, 4.0]
path.setLineDash(dashPattern, count: dashPattern.count, phase: 0)
// <<
path.lineWidth = 1
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 100, y: 100))
path.stroke()
}
}
Как много раз говорилось в этой теме, вы можете поиграть с pattern
и phase
чтобы получить сложную пунктирную линию.
Надеюсь это поможет