49

ドック内の何かを右クリックしたときに、Mac OS X に似た「吹き出し」効果を得ようとしています。これが私が今持っているものです:

代替テキスト

下部の「三角形」の部分を取得する必要があります。そのようなものを描き、その周りに境界線を引く方法はありますか? こちらはiPhoneアプリになります。

前もって感謝します!

編集: Brad Larson に感謝します。現在の様子は次のとおりです。 代替テキスト

4

12 に答える 12

53

私は実際にこの正確な形状を以前に描いたことがあります(下部に三角形がある角丸長方形)。私が使用した Quartz 描画コードは次のとおりです。

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);     

グラデーションや単純な色よりも複雑なその他の塗りつぶしを使用しない場合は、最後のクリッピング パスを省略できます。

于 2010-12-14T20:45:18.380 に答える
14

UIBezierPath を作成する Swift 2 コード:

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)

ここに画像の説明を入力

于 2015-10-28T10:05:57.017 に答える
13

おそらく、より単純な質問は、「これを実行するコードは既にありますか」であり、答えは「はい」です。

見よMAAttachedWindow

代替テキスト

確かに、「アタッチされたウィンドウ」の動作全体が必要ない場合もありますが、少なくとも描画コードは既にそこにあります。(そして Matt Gemmell のコードは高品質のものです)

于 2010-12-14T21:11:18.817 に答える
7

これを達成できる可能性のある2つの方法があります。

  1. 適切な場所に三角形の画像を含むUIImageViewを追加します。背景を遮らないように、画像の残りの部分が透明であることを確認してください。
  2. UIViewのメソッドをオーバーライドしてdrawRect:、ビューをカスタム描画します。次に、三角形の線形パスコンポーネントを追加し、必要に応じてパスを塗りつぶして境界を設定できます。

を使用して単純な三角形を描くには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リファレンスを参照してください。

于 2010-12-14T17:33:02.417 に答える
3

Brad Larsonの回答に基づいてswift 2.0を使用している場合

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)
}
于 2016-01-10T17:06:39.760 に答える
1

下の画像のポップアップ メニューの三角形を参照してください。これは Core Graphics 関数で描画され、完全にスケーラブルです。

代替テキスト

正三角形を行うには、次のようにします (古い学校の関数名、申し訳ありません)。

#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) は決して変わらないので、その定義に最適化しました。

于 2010-12-14T18:40:08.563 に答える
-3

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];
于 2010-12-14T17:34:36.147 に答える