如何在iPhone上绘制“语音泡沫”?
-
10-10-2019 - |
题
当您右键单击码头中的某些内容时,我正在尝试获得类似于Mac OS X中的“语音气泡”效果。这是我现在拥有的:
我需要获得下部的“三角”部分。有什么办法可以画出这样的东西并在它周围获得边界?这将是一个 苹果手机 应用程序。
提前致谢!
编辑:非常感谢布拉德·拉尔森(Brad Larson),这就是现在的样子:
解决方案
实际上,我在以前绘制过这种确切的形状(圆形的矩形,底部有一个指向三角形)。我使用的石英绘图代码如下:
CGRect currentFrame = self.bounds;
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineWidth(context, strokeWidth);
CGContextSetStrokeColorWithColor(context, [MyPopupLayer popupBorderColor]);
CGContextSetFillColorWithColor(context, [MyPopupLayer popupBackgroundColor]);
// Draw and fill the bubble
CGContextBeginPath(context);
CGContextMoveToPoint(context, borderRadius + strokeWidth + 0.5f, strokeWidth + HEIGHTOFPOPUPTRIANGLE + 0.5f);
CGContextAddLineToPoint(context, round(currentFrame.size.width / 2.0f - WIDTHOFPOPUPTRIANGLE / 2.0f) + 0.5f, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5f);
CGContextAddLineToPoint(context, round(currentFrame.size.width / 2.0f) + 0.5f, strokeWidth + 0.5f);
CGContextAddLineToPoint(context, round(currentFrame.size.width / 2.0f + WIDTHOFPOPUPTRIANGLE / 2.0f) + 0.5f, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5f);
CGContextAddArcToPoint(context, currentFrame.size.width - strokeWidth - 0.5f, strokeWidth + HEIGHTOFPOPUPTRIANGLE + 0.5f, currentFrame.size.width - strokeWidth - 0.5f, currentFrame.size.height - strokeWidth - 0.5f, borderRadius - strokeWidth);
CGContextAddArcToPoint(context, currentFrame.size.width - strokeWidth - 0.5f, currentFrame.size.height - strokeWidth - 0.5f, round(currentFrame.size.width / 2.0f + WIDTHOFPOPUPTRIANGLE / 2.0f) - strokeWidth + 0.5f, currentFrame.size.height - strokeWidth - 0.5f, borderRadius - strokeWidth);
CGContextAddArcToPoint(context, strokeWidth + 0.5f, currentFrame.size.height - strokeWidth - 0.5f, strokeWidth + 0.5f, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5f, borderRadius - strokeWidth);
CGContextAddArcToPoint(context, strokeWidth + 0.5f, strokeWidth + HEIGHTOFPOPUPTRIANGLE + 0.5f, currentFrame.size.width - strokeWidth - 0.5f, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5f, borderRadius - strokeWidth);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
// Draw a clipping path for the fill
CGContextBeginPath(context);
CGContextMoveToPoint(context, borderRadius + strokeWidth + 0.5f, round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50f) + 0.5f);
CGContextAddArcToPoint(context, currentFrame.size.width - strokeWidth - 0.5f, round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50f) + 0.5f, currentFrame.size.width - strokeWidth - 0.5f, currentFrame.size.height - strokeWidth - 0.5f, borderRadius - strokeWidth);
CGContextAddArcToPoint(context, currentFrame.size.width - strokeWidth - 0.5f, currentFrame.size.height - strokeWidth - 0.5f, round(currentFrame.size.width / 2.0f + WIDTHOFPOPUPTRIANGLE / 2.0f) - strokeWidth + 0.5f, currentFrame.size.height - strokeWidth - 0.5f, borderRadius - strokeWidth);
CGContextAddArcToPoint(context, strokeWidth + 0.5f, currentFrame.size.height - strokeWidth - 0.5f, strokeWidth + 0.5f, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5f, borderRadius - strokeWidth);
CGContextAddArcToPoint(context, strokeWidth + 0.5f, round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50f) + 0.5f, currentFrame.size.width - strokeWidth - 0.5f, round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50f) + 0.5f, borderRadius - strokeWidth);
CGContextClosePath(context);
CGContextClip(context);
如果您不使用梯度或其他比简单颜色更复杂的填充,则可以忽略末端的剪辑路径。
其他提示
也许更简单的问题是“是否已经为我做到这一点了”,答案为“是”。
当然,您可能不希望整个“附带的窗口”行为,但至少绘图代码已经存在。 (而马特·吉梅尔(Matt Gemmell)的代码是高质量的东西)
Swift 2代码创建UibezierPath:
var borderWidth : CGFloat = 4 // Should be less or equal to the `radius` property
var radius : CGFloat = 10
var triangleHeight : CGFloat = 15
private func bubblePathForContentSize(contentSize: CGSize) -> UIBezierPath {
let rect = CGRectMake(0, 0, contentSize.width, contentSize.height).offsetBy(dx: radius, dy: radius + triangleHeight)
let path = UIBezierPath();
let radius2 = radius - borderWidth / 2 // Radius adjasted for the border width
path.moveToPoint(CGPointMake(rect.maxX - triangleHeight * 2, rect.minY - radius2))
path.addLineToPoint(CGPointMake(rect.maxX - triangleHeight, rect.minY - radius2 - triangleHeight))
path.addArcWithCenter(CGPointMake(rect.maxX, rect.minY), radius: radius2, startAngle: CGFloat(-M_PI_2), endAngle: 0, clockwise: true)
path.addArcWithCenter(CGPointMake(rect.maxX, rect.maxY), radius: radius2, startAngle: 0, endAngle: CGFloat(M_PI_2), clockwise: true)
path.addArcWithCenter(CGPointMake(rect.minX, rect.maxY), radius: radius2, startAngle: CGFloat(M_PI_2), endAngle: CGFloat(M_PI), clockwise: true)
path.addArcWithCenter(CGPointMake(rect.minX, rect.minY), radius: radius2, startAngle: CGFloat(M_PI), endAngle: CGFloat(-M_PI_2), clockwise: true)
path.closePath()
return path
}
现在,您可以通过这条路做任何您想做的事情。例如,与CashApelayer一起使用:
let bubbleLayer = CAShapeLayer()
bubbleLayer.path = bubblePathForContentSize(contentView.bounds.size).CGPath
bubbleLayer.fillColor = fillColor.CGColor
bubbleLayer.strokeColor = borderColor.CGColor
bubbleLayer.lineWidth = borderWidth
bubbleLayer.position = CGPoint.zero
myView.layer.addSublayer(bubbleLayer)
我在这里寻找一个解决方案,以在现有视图中绘制“箭头”。
我很高兴分享一些我希望有用的代码 - Swift 2.3兼容 -
public extension UIView {
public enum PeakSide: Int {
case Top
case Left
case Right
case Bottom
}
public func addPikeOnView(side side: PeakSide, size: CGFloat = 10.0) {
self.layoutIfNeeded()
let peakLayer = CAShapeLayer()
var path: CGPathRef?
switch side {
case .Top:
path = self.makePeakPathWithRect(self.bounds, topSize: size, rightSize: 0.0, bottomSize: 0.0, leftSize: 0.0)
case .Left:
path = self.makePeakPathWithRect(self.bounds, topSize: 0.0, rightSize: 0.0, bottomSize: 0.0, leftSize: size)
case .Right:
path = self.makePeakPathWithRect(self.bounds, topSize: 0.0, rightSize: size, bottomSize: 0.0, leftSize: 0.0)
case .Bottom:
path = self.makePeakPathWithRect(self.bounds, topSize: 0.0, rightSize: 0.0, bottomSize: size, leftSize: 0.0)
}
peakLayer.path = path
let color = (self.backgroundColor ?? .clearColor()).CGColor
peakLayer.fillColor = color
peakLayer.strokeColor = color
peakLayer.lineWidth = 1
peakLayer.position = CGPoint.zero
self.layer.insertSublayer(peakLayer, atIndex: 0)
}
func makePeakPathWithRect(rect: CGRect, topSize ts: CGFloat, rightSize rs: CGFloat, bottomSize bs: CGFloat, leftSize ls: CGFloat) -> CGPathRef {
// P3
// / \
// P1 -------- P2 P4 -------- P5
// | |
// | |
// P16 P6
// / \
// P15 P7
// \ /
// P14 P8
// | |
// | |
// P13 ------ P12 P10 -------- P9
// \ /
// P11
let centerX = rect.width / 2
let centerY = rect.height / 2
var h: CGFloat = 0
let path = CGPathCreateMutable()
var points: [CGPoint] = []
// P1
points.append(CGPointMake(rect.origin.x, rect.origin.y))
// Points for top side
if ts > 0 {
h = ts * sqrt(3.0) / 2
let x = rect.origin.x + centerX
let y = rect.origin.y
points.append(CGPointMake(x - ts, y))
points.append(CGPointMake(x, y - h))
points.append(CGPointMake(x + ts, y))
}
// P5
points.append(CGPointMake(rect.origin.x + rect.width, rect.origin.y))
// Points for right side
if rs > 0 {
h = rs * sqrt(3.0) / 2
let x = rect.origin.x + rect.width
let y = rect.origin.y + centerY
points.append(CGPointMake(x, y - rs))
points.append(CGPointMake(x + h, y))
points.append(CGPointMake(x, y + rs))
}
// P9
points.append(CGPointMake(rect.origin.x + rect.width, rect.origin.y + rect.height))
// Point for bottom side
if bs > 0 {
h = bs * sqrt(3.0) / 2
let x = rect.origin.x + centerX
let y = rect.origin.y + rect.height
points.append(CGPointMake(x + bs, y))
points.append(CGPointMake(x, y + h))
points.append(CGPointMake(x - bs, y))
}
// P13
points.append(CGPointMake(rect.origin.x, rect.origin.y + rect.height))
// Point for left side
if ls > 0 {
h = ls * sqrt(3.0) / 2
let x = rect.origin.x
let y = rect.origin.y + centerY
points.append(CGPointMake(x, y + ls))
points.append(CGPointMake(x - h, y))
points.append(CGPointMake(x, y - ls))
}
let startPoint = points.removeFirst()
self.startPath(path: path, onPoint: startPoint)
for point in points {
self.addPoint(point, toPath: path)
}
self.addPoint(startPoint, toPath: path)
return path
}
private func startPath(path path: CGMutablePath, onPoint point: CGPoint) {
CGPathMoveToPoint(path, nil, point.x, point.y)
}
private func addPoint(point: CGPoint, toPath path: CGMutablePath) {
CGPathAddLineToPoint(path, nil, point.x, point.y)
}
}
这样,您可以以各种视图来调用此内容:
let view = UIView(frame: frame)
view.addPikeOnView(side: .Top)
将来,我将增加派克位置的偏移。
- 是的,名字绝对可以改进!
Swift 3版本
public extension UIView {
public enum PeakSide: Int {
case Top
case Left
case Right
case Bottom
}
public func addPikeOnView( side: PeakSide, size: CGFloat = 10.0) {
self.layoutIfNeeded()
let peakLayer = CAShapeLayer()
var path: CGPath?
switch side {
case .Top:
path = self.makePeakPathWithRect(rect: self.bounds, topSize: size, rightSize: 0.0, bottomSize: 0.0, leftSize: 0.0)
case .Left:
path = self.makePeakPathWithRect(rect: self.bounds, topSize: 0.0, rightSize: 0.0, bottomSize: 0.0, leftSize: size)
case .Right:
path = self.makePeakPathWithRect(rect: self.bounds, topSize: 0.0, rightSize: size, bottomSize: 0.0, leftSize: 0.0)
case .Bottom:
path = self.makePeakPathWithRect(rect: self.bounds, topSize: 0.0, rightSize: 0.0, bottomSize: size, leftSize: 0.0)
}
peakLayer.path = path
let color = (self.backgroundColor?.cgColor)
peakLayer.fillColor = color
peakLayer.strokeColor = color
peakLayer.lineWidth = 1
peakLayer.position = CGPoint.zero
self.layer.insertSublayer(peakLayer, at: 0)
}
func makePeakPathWithRect(rect: CGRect, topSize ts: CGFloat, rightSize rs: CGFloat, bottomSize bs: CGFloat, leftSize ls: CGFloat) -> CGPath {
// P3
// / \
// P1 -------- P2 P4 -------- P5
// | |
// | |
// P16 P6
// / \
// P15 P7
// \ /
// P14 P8
// | |
// | |
// P13 ------ P12 P10 -------- P9
// \ /
// P11
let centerX = rect.width / 2
let centerY = rect.height / 2
var h: CGFloat = 0
let path = CGMutablePath()
var points: [CGPoint] = []
// P1
points.append(CGPoint(x:rect.origin.x,y: rect.origin.y))
// Points for top side
if ts > 0 {
h = ts * sqrt(3.0) / 2
let x = rect.origin.x + centerX
let y = rect.origin.y
points.append(CGPoint(x:x - ts,y: y))
points.append(CGPoint(x:x,y: y - h))
points.append(CGPoint(x:x + ts,y: y))
}
// P5
points.append(CGPoint(x:rect.origin.x + rect.width,y: rect.origin.y))
// Points for right side
if rs > 0 {
h = rs * sqrt(3.0) / 2
let x = rect.origin.x + rect.width
let y = rect.origin.y + centerY
points.append(CGPoint(x:x,y: y - rs))
points.append(CGPoint(x:x + h,y: y))
points.append(CGPoint(x:x,y: y + rs))
}
// P9
points.append(CGPoint(x:rect.origin.x + rect.width,y: rect.origin.y + rect.height))
// Point for bottom side
if bs > 0 {
h = bs * sqrt(3.0) / 2
let x = rect.origin.x + centerX
let y = rect.origin.y + rect.height
points.append(CGPoint(x:x + bs,y: y))
points.append(CGPoint(x:x,y: y + h))
points.append(CGPoint(x:x - bs,y: y))
}
// P13
points.append(CGPoint(x:rect.origin.x, y: rect.origin.y + rect.height))
// Point for left sidey:
if ls > 0 {
h = ls * sqrt(3.0) / 2
let x = rect.origin.x
let y = rect.origin.y + centerY
points.append(CGPoint(x:x,y: y + ls))
points.append(CGPoint(x:x - h,y: y))
points.append(CGPoint(x:x,y: y - ls))
}
let startPoint = points.removeFirst()
self.startPath(path: path, onPoint: startPoint)
for point in points {
self.addPoint(point: point, toPath: path)
}
self.addPoint(point: startPoint, toPath: path)
return path
}
private func startPath( path: CGMutablePath, onPoint point: CGPoint) {
path.move(to: CGPoint(x: point.x, y: point.y))
}
private func addPoint(point: CGPoint, toPath path: CGMutablePath) {
path.addLine(to: CGPoint(x: point.x, y: point.y))
}
}
您可能有两种方法可以实现这一目标:
- 添加一个 UIImageView 在正确的位置带有三角形图像。确保图像的其余部分是透明的,以免阻止您的背景。
- 覆盖
drawRect:
您在UIView上的方法自定义绘制视图。然后,您可以为三角形添加线性路径组件,并根据需要填充和接壤。
使用一个简单的三角 drawRect:
, ,您可能会做这样的事情。该片段将绘制一个三角形,该三角形指向您视图的底部。
// Get the context
CGContextRef context = UIGraphicsGetCurrentContext();
// Pick colors
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
// Define triangle dimensions
CGFloat baseWidth = 30.0;
CGFloat height = 20.0;
// Define path
CGContextMoveToPoint(context, self.bounds.size.width / 2.0 - baseWidth / 2.0,
self.bounds.size.height - height);
CGContextAddLineToPoint(context, self.bounds.size.width / 2.0 + baseWidth / 2.0,
self.bounds.size.height - height);
CGContextAddLineToPoint(context, self.bounds.size.width / 2.0,
self.bounds.size.height);
// Finalize and draw using path
CGContextClosePath(context);
CGContextStrokePath(context);
有关更多信息,请参阅 CGContext参考.
对于那些根据答案使用Swift 2.0的人 布拉德·拉尔森(Brad Larson)
override func drawRect(rect: CGRect) {
super.drawRect(rect) // optional if a direct UIView-subclass, should be called otherwise.
let HEIGHTOFPOPUPTRIANGLE:CGFloat = 20.0
let WIDTHOFPOPUPTRIANGLE:CGFloat = 40.0
let borderRadius:CGFloat = 8.0
let strokeWidth:CGFloat = 3.0
// Get the context
let context: CGContextRef = UIGraphicsGetCurrentContext()!
CGContextTranslateCTM(context, 0.0, self.bounds.size.height)
CGContextScaleCTM(context, 1.0, -1.0)
//
let currentFrame: CGRect = self.bounds
CGContextSetLineJoin(context, CGLineJoin.Round)
CGContextSetLineWidth(context, strokeWidth)
CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor)
CGContextSetFillColorWithColor(context, UIColor.blackColor().CGColor)
// Draw and fill the bubble
CGContextBeginPath(context)
CGContextMoveToPoint(context, borderRadius + strokeWidth + 0.5, strokeWidth + HEIGHTOFPOPUPTRIANGLE + 0.5)
CGContextAddLineToPoint(context, round(currentFrame.size.width / 2.0 - WIDTHOFPOPUPTRIANGLE / 2.0) + 0.5, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5)
CGContextAddLineToPoint(context, round(currentFrame.size.width / 2.0) + 0.5, strokeWidth + 0.5)
CGContextAddLineToPoint(context, round(currentFrame.size.width / 2.0 + WIDTHOFPOPUPTRIANGLE / 2.0) + 0.5, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5)
CGContextAddArcToPoint(context, currentFrame.size.width - strokeWidth - 0.5, strokeWidth + HEIGHTOFPOPUPTRIANGLE + 0.5, currentFrame.size.width - strokeWidth - 0.5, currentFrame.size.height - strokeWidth - 0.5, borderRadius - strokeWidth)
CGContextAddArcToPoint(context, currentFrame.size.width - strokeWidth - 0.5, currentFrame.size.height - strokeWidth - 0.5, round(currentFrame.size.width / 2.0 + WIDTHOFPOPUPTRIANGLE / 2.0) - strokeWidth + 0.5, currentFrame.size.height - strokeWidth - 0.5, borderRadius - strokeWidth)
CGContextAddArcToPoint(context, strokeWidth + 0.5, currentFrame.size.height - strokeWidth - 0.5, strokeWidth + 0.5, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5, borderRadius - strokeWidth)
CGContextAddArcToPoint(context, strokeWidth + 0.5, strokeWidth + HEIGHTOFPOPUPTRIANGLE + 0.5, currentFrame.size.width - strokeWidth - 0.5, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5, borderRadius - strokeWidth)
CGContextClosePath(context)
CGContextDrawPath(context, CGPathDrawingMode.FillStroke)
// Draw a clipping path for the fill
CGContextBeginPath(context)
CGContextMoveToPoint(context, borderRadius + strokeWidth + 0.5, round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50) + 0.5)
CGContextAddArcToPoint(context, currentFrame.size.width - strokeWidth - 0.5, round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50) + 0.5, currentFrame.size.width - strokeWidth - 0.5, currentFrame.size.height - strokeWidth - 0.5, borderRadius - strokeWidth)
CGContextAddArcToPoint(context, currentFrame.size.width - strokeWidth - 0.5, currentFrame.size.height - strokeWidth - 0.5, round(currentFrame.size.width / 2.0 + WIDTHOFPOPUPTRIANGLE / 2.0) - strokeWidth + 0.5, currentFrame.size.height - strokeWidth - 0.5, borderRadius - strokeWidth)
CGContextAddArcToPoint(context, strokeWidth + 0.5, currentFrame.size.height - strokeWidth - 0.5, strokeWidth + 0.5, HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5, borderRadius - strokeWidth)
CGContextAddArcToPoint(context, strokeWidth + 0.5, round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50) + 0.5, currentFrame.size.width - strokeWidth - 0.5, round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50) + 0.5, borderRadius - strokeWidth)
CGContextClosePath(context)
CGContextClip(context)
}
Swift 4更新
这是一个迅速的4版 AVT的原始代码.
private func bubblePathForContentSize(contentSize: CGSize) -> UIBezierPath {
let rect = CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height)).offsetBy(dx: radius, dy: radius + triangleHeight)
let path = UIBezierPath();
let radius2 = radius - borderWidth / 2 // Radius adjasted for the border width
path.move(to: CGPoint(x: rect.maxX - triangleHeight * 2, y: rect.minY - radius2))
path.addLine(to: CGPoint(x: rect.maxX - triangleHeight, y: rect.minY - radius2 - triangleHeight))
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY),
radius: radius2,
startAngle: CGFloat(-(Double.pi/2)), endAngle: 0, clockwise: true)
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.maxY),
radius: radius2,
startAngle: 0, endAngle: CGFloat(Double.pi/2), clockwise: true)
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY),
radius: radius2,
startAngle: CGFloat(Double.pi/2),endAngle: CGFloat(Double.pi), clockwise: true)
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY),
radius: radius2,
startAngle: CGFloat(Double.pi), endAngle: CGFloat(-(Double.pi/2)), clockwise: true)
path.close()
return path
}
//Example usage:
let bubbleLayer = CAShapeLayer()
bubbleLayer.path = bubblePathForContentSize(contentView.bounds.size).CGPath
bubbleLayer.fillColor = fillColor.CGColor
bubbleLayer.strokeColor = borderColor.CGColor
bubbleLayer.lineWidth = borderWidth
bubbleLayer.position = CGPoint.zero
myView.layer.addSublayer(bubbleLayer)
请参阅下图中的“弹出菜单”弹出菜单上的三角形,这是用核心图形函数绘制的,并且是完全可扩展的。
这样做以执行等边三角形(老式功能名称,对不起):
#define triH(v) (v * 0.866)
func(CGContextRef inContext, CGRect arrowRect, CustomPushButtonData* controlData) {
// Draw the triangle
float arrowXstart, arrowYstart;
float arrowXpos, arrowYpos, arrowHpos;
if (controlData->controlEnabled && controlData->controlActive) {
CGContextSetRGBFillColor(inContext, 0., 0., 0., 1.);
} else {
CGContextSetRGBFillColor(inContext, 0., 0., 0., 0.5);
}
arrowHpos = triH(arrowRect.size.height);
// Point C
CGContextBeginPath(inContext);
arrowXstart = arrowXpos = (arrowRect.origin.x + ((float)(arrowRect.size.width / 2.) - (arrowSize / 2.)));
arrowYstart = arrowYpos = (arrowRect.origin.y + (float)((arrowRect.size.height / 2.) - (float)(arrowHpos / 2.)));
CGContextMoveToPoint(inContext, arrowXpos, arrowYpos);
// Point A
arrowXpos += arrowSize;
CGContextAddLineToPoint(inContext, arrowXpos, arrowYpos);
// Point B
arrowYpos += arrowHpos;
arrowXpos -= (float)(arrowSize / 2.0);
CGContextAddLineToPoint(inContext, arrowXpos, arrowYpos);
// Point C
CGContextAddLineToPoint(inContext, arrowXstart, arrowYstart);
CGContextClosePath(inContext);
CGContextFillPath(inContext);
}
请注意,TRIH(x)func是一种优化公式,用于计算均衡三角形的高度,例如h = 1/2 * sqrt(3) * x。由于1/2 * SQRT(3)从不更改,因此我将其优化为定义。
如果有人来寻找Swift 3的答案,那么这会解决问题!感谢那些在我之前做出的贡献的人,这是可爱的代码!
let rRect = CGRect(x: start.x, y: start.y, width: defaultHeightWidth.0, height: defaultHeightWidth.1)
context?.translateBy(x: 0, y: rRect.size.height - 3)
context?.scaleBy(x: 1.0, y: -1.0)
context?.setLineJoin(.bevel)
context?.setLineWidth(strokeWidth)
context?.setStrokeColor(UIColor.black.cgColor)
context?.setFillColor(UIColor.white.cgColor)
// draw and fill the bubble
context?.beginPath()
context?.move(to: CGPoint(x: borderRadius + strokeWidth + 0.5, y: strokeWidth + triangleHeight + 0.5))
context?.addLine(to: CGPoint(x: round(rRect.size.width / 2.0 - triangleWidth / 2.0) + 0.5, y: triangleHeight + strokeWidth + 0.5))
context?.addLine(to: CGPoint(x: round(rRect.size.width / 2.0) + 0.5, y: strokeWidth + 0.5))
context?.addLine(to: CGPoint(x: round(rRect.size.width / 2.0 + triangleWidth / 2.0), y: triangleHeight + strokeWidth + 0.5))
context?.addArc(tangent1End: CGPoint(x: rRect.size.width - strokeWidth - 0.5, y: strokeWidth + triangleHeight + 0.5), tangent2End: CGPoint(x: rRect.size.width - strokeWidth - 0.5, y: rRect.size.height - strokeWidth - 0.5), radius: borderRadius - strokeWidth)
context?.addArc(tangent1End: CGPoint(x: rRect.size.width - strokeWidth - 0.5, y: rRect.size.height - strokeWidth - 0.5), tangent2End: CGPoint(x: round(rRect.size.width / 2.0 + triangleWidth / 2.0) - strokeWidth + 0.5, y: rRect.size.height - strokeWidth - 0.5), radius: borderRadius - strokeWidth)
context?.addArc(tangent1End: CGPoint(x: strokeWidth + 0.5, y: rRect.size.height - strokeWidth - 0.5), tangent2End: CGPoint(x: strokeWidth + 0.5, y: triangleHeight + strokeWidth + 0.5), radius: borderRadius - strokeWidth)
context?.addArc(tangent1End: CGPoint(x: strokeWidth + 0.5, y: strokeWidth + triangleHeight + 0.5), tangent2End: CGPoint(x: rRect.size.width - strokeWidth - 0.5, y: triangleHeight + strokeWidth + 0.5), radius: borderRadius - strokeWidth)
context?.closePath()
context?.drawPath(using: .fillStroke)
就我而言 triangleWidth = 10
和 triangleHeight = 5
对于比OPS版本中的视图要小得多。
这是Swift 3解决方案 布拉德·拉尔森(Brad Larson)
override func draw(_ rect: CGRect) {
super.draw(rect) // optional if a direct UIView-subclass, should be called otherwise.
let HEIGHTOFPOPUPTRIANGLE:CGFloat = 20.0
let WIDTHOFPOPUPTRIANGLE:CGFloat = 40.0
let borderRadius:CGFloat = 8.0
let strokeWidth:CGFloat = 3.0
// Get the context
let context: CGContext = UIGraphicsGetCurrentContext()!
context.translateBy(x: 0.0, y: self.bounds.size.height)
context.scaleBy(x: 1.0, y: -1.0)
//
let currentFrame: CGRect = self.bounds
context.setLineJoin(CGLineJoin.round)
context.setLineWidth(strokeWidth)
context.setStrokeColor(UIColor.white.cgColor)
context.setFillColor(UIColor.black.cgColor)
// Draw and fill the bubble
context.beginPath()
context.move(to: CGPoint(x: borderRadius + strokeWidth + 0.5, y: strokeWidth + HEIGHTOFPOPUPTRIANGLE + 0.5))
context.addLine(to: CGPoint(x: round(currentFrame.size.width / 2.0 - WIDTHOFPOPUPTRIANGLE / 2.0) + 0.5, y: HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5))
context.addLine(to: CGPoint(x: round(currentFrame.size.width / 2.0) + 0.5, y: strokeWidth + 0.5))
context.addLine(to: CGPoint(x: round(currentFrame.size.width / 2.0 + WIDTHOFPOPUPTRIANGLE / 2.0) + 0.5, y: HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5))
context.addArc(tangent1End: CGPoint(x: currentFrame.size.width - strokeWidth - 0.5, y: strokeWidth + HEIGHTOFPOPUPTRIANGLE + 0.5), tangent2End: CGPoint(x: currentFrame.size.width - strokeWidth - 0.5, y: currentFrame.size.height - strokeWidth - 0.5), radius: borderRadius - strokeWidth)
context.addArc(tangent1End: CGPoint(x: currentFrame.size.width - strokeWidth - 0.5, y: currentFrame.size.height - strokeWidth - 0.5) , tangent2End: CGPoint(x: round(currentFrame.size.width / 2.0 + WIDTHOFPOPUPTRIANGLE / 2.0) - strokeWidth + 0.5, y: currentFrame.size.height - strokeWidth - 0.5) , radius: borderRadius - strokeWidth)
context.addArc(tangent1End: CGPoint(x: strokeWidth + 0.5, y: currentFrame.size.height - strokeWidth - 0.5), tangent2End: CGPoint(x: strokeWidth + 0.5, y: HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5), radius: borderRadius - strokeWidth)
context.addArc(tangent1End: CGPoint(x: strokeWidth + 0.5, y :strokeWidth + HEIGHTOFPOPUPTRIANGLE + 0.5), tangent2End: CGPoint(x: currentFrame.size.width - strokeWidth - 0.5 ,y: HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5), radius: borderRadius - strokeWidth)
context.closePath()
context.drawPath(using: CGPathDrawingMode.fillStroke)
// Draw a clipping path for the fill
context.beginPath()
context.move(to: CGPoint(x: borderRadius + strokeWidth + 0.5, y: round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50) + 0.5))
context.addArc(tangent1End: CGPoint(x: currentFrame.size.width - strokeWidth - 0.5, y: round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50) + 0.5), tangent2End: CGPoint(x: currentFrame.size.width - strokeWidth - 0.5, y: currentFrame.size.height - strokeWidth - 0.5), radius: borderRadius - strokeWidth)
context.addArc(tangent1End: CGPoint(x: currentFrame.size.width - strokeWidth - 0.5, y: currentFrame.size.height - strokeWidth - 0.5) , tangent2End: CGPoint(x: round(currentFrame.size.width / 2.0 + WIDTHOFPOPUPTRIANGLE / 2.0) - strokeWidth + 0.5, y: currentFrame.size.height - strokeWidth - 0.5), radius: borderRadius - strokeWidth)
context.addArc(tangent1End: CGPoint(x: strokeWidth + 0.5, y: currentFrame.size.height - strokeWidth - 0.5), tangent2End: CGPoint(x: strokeWidth + 0.5, y: HEIGHTOFPOPUPTRIANGLE + strokeWidth + 0.5), radius: borderRadius - strokeWidth)
context.addArc(tangent1End: CGPoint(x: strokeWidth + 0.5, y: round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50) + 0.5), tangent2End: CGPoint(x: currentFrame.size.width - strokeWidth - 0.5, y: round((currentFrame.size.height + HEIGHTOFPOPUPTRIANGLE) * 0.50) + 0.5), radius: borderRadius - strokeWidth)
context.closePath()
context.clip()
}
我可能会在Photoshop中制作整个图像(包括三角形),然后在适当的时间在屏幕上使用:
CGRect myRect = CGRectMake(10.0f, 0.0f, 300.0f, 420.0f);
UIImageView *myImage = [[UIImageView alloc] initWithFrame:myRect];
[myImage setImage:[UIImage imageNamed:@"ThisIsMyImageName.png"]];
myImage.opaque = YES;
[self.view addSubview:myImage];
[myImage release];