2

I am trying to create a resizable version of the following image (40x28, the small triangle at the bottom should be centered horizontally):

enter image description here

UIEdgeInsets imageInsets = UIEdgeInsetsMake(4.0f, 4.0f, 8.0f, 4.0f);
UIImage *pinImage = [[UIImage imageNamed:@"map_pin"] resizableImageWithCapInsets:imageInsets];                                                                       

As per the documentation, this simple approach won't work since my triangle falls into resizable area (fixed height but scalable width):

enter image description here

This will cause the same problem as described in this very similar SO post - duplicated (tiled) triangles. But unfortunately their solution doesn't work for my particular case since excluding the triangle from scalable area causes its misplacement and it won't be centered horizontally due to obvious reasons.

So I wonder... is it possible to achieve my goal using the [UIImage resizableImageWithCapInsets:] method at all? Or may be I should try something else like drawing everything in the code instead?

Basically, I would like to implement something like Info Windows from Google Maps SDK:

enter image description here

Community
  • 1
  • 1
fraggjkee
  • 3,524
  • 2
  • 32
  • 38
  • 1
    Did u try drawing the image using bezierPAth, and let bezier path resize based on the view frame > – Teja Nandamuri Aug 10 '16 at 14:25
  • Not yet, but it looks like I'll have to go in this direction. – fraggjkee Aug 10 '16 at 14:28
  • 1
    If you still want a UIImage, you may want to cut it into pieces. One for the arrow, the other for the rectangle. and compute them. A custom UIView (or UIImageView) with constraints, border with leading/trailing/top to superview, and bottom at distance equal to height to superview, and the arrow fixed width, centered, fixed height and bottom to superview? – Larme Aug 10 '16 at 14:32
  • @Larme sounds like the simplest solutions, thanks for the idea. But I decided to go with a more complex but more flexible version - `UIBezierPath`. – fraggjkee Aug 11 '16 at 14:01

2 Answers2

1

You can achieve that by using UIBezierPath.The following code in swift but you should be able to achieve the same thing in Objective-C

Instead of starting the code with a straight line :

path.moveToPoint(CGPoint(x: 300, y: 0))

I instead start with an arc (upper right):

path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle: 0 , endAngle: CGFloat(M_PI/2)  , clockwise: true) //1st rounded corner

and by doing this, I have four rounded corners and I just need to add a straight line at the end of the code right before:

path.closePath()  

Here is the code and a screenshot:

let path = UIBezierPath()
path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle: 0 , endAngle: CGFloat(M_PI/2)  , clockwise: true) //1st rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 50), radius:10, startAngle: CGFloat(2 * M_PI / 3), endAngle:CGFloat(M_PI) , clockwise: true)// 2rd rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 10), radius:10, startAngle: CGFloat(M_PI), endAngle:CGFloat(3 * M_PI / 2), clockwise: true)// 3rd rounded corner
// little triangle
path.addLineToPoint(CGPoint(x:240 , y:0))
path.addLineToPoint(CGPoint(x: 245, y: -10))
path.addLineToPoint(CGPoint(x:250, y: 0))
path.addArcWithCenter(CGPoint(x: 290, y: 10), radius: 10, startAngle: CGFloat(3 * M_PI / 2), endAngle: CGFloat(2 * M_PI ), clockwise: true)
path.addLineToPoint(CGPoint(x:300 , y:50))
path.closePath()

enter image description here

Assam AlZookery
  • 479
  • 4
  • 15
1

I decided to go with UIBezierPath as Assam Al-Zookery suggested. Here is the complete code in Objective-C (should be reusable and flexible enough):

@implementation TestView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.clipsToBounds = YES;
        self.opaque = NO;
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    UIBezierPath *path = [UIBezierPath bezierPath];
    path.lineWidth = 2.0f;
    path.lineJoinStyle = kCGLineJoinRound;

    [[UIColor blackColor] setStroke];
    [[UIColor redColor] setFill];

    CGFloat arcRadius = 5.0f;
    CGFloat padding = 5.0f;
    CGFloat triangleSide = 20.0f;
    CGFloat triangleHeight = ((triangleSide * sqrtf(3.0f)) / 2);
    CGFloat rectHeight = CGRectGetHeight(rect) - triangleHeight - padding - (arcRadius * 2);

    // Top left corner
    [path addArcWithCenter:CGPointMake(arcRadius + padding, arcRadius + padding)
                    radius:arcRadius
                startAngle:DEGREES_TO_RADIANS(180)
                  endAngle:DEGREES_TO_RADIANS(270)
                 clockwise:YES];

    // Top edge
    [path addLineToPoint:CGPointMake(CGRectGetWidth(rect) - arcRadius - padding, path.currentPoint.y)];

    // Top right corner
    [path addArcWithCenter:CGPointMake(path.currentPoint.x, path.currentPoint.y + arcRadius)
                    radius:arcRadius
                startAngle:DEGREES_TO_RADIANS(270)
                  endAngle:DEGREES_TO_RADIANS(0)
                 clockwise:YES];

    // Right edge
    [path addLineToPoint:CGPointMake(CGRectGetWidth(rect) - padding, rectHeight + padding)];

    // Bottom right corner
    [path addArcWithCenter:CGPointMake(path.currentPoint.x - arcRadius, path.currentPoint.y)
                    radius:arcRadius
                startAngle:DEGREES_TO_RADIANS(0)
                  endAngle:DEGREES_TO_RADIANS(90)
                 clockwise:YES];

    // Bottom edge (right part)
    [path addLineToPoint:CGPointMake((CGRectGetWidth(rect) / 2) + (triangleSide / 2), path.currentPoint.y)];

    // Triangle
    [path addLineToPoint:CGPointMake(path.currentPoint.x - (triangleSide / 2), path.currentPoint.y + triangleHeight)];
    [path addLineToPoint:CGPointMake(path.currentPoint.x - (triangleSide / 2), path.currentPoint.y - triangleHeight)];

    // Bottom edge (left part)
    [path addLineToPoint:CGPointMake(padding + arcRadius, path.currentPoint.y)];

    // Bottom left corner
    [path addArcWithCenter:CGPointMake(path.currentPoint.x, path.currentPoint.y - arcRadius)
                    radius:arcRadius
                startAngle:DEGREES_TO_RADIANS(90)
                  endAngle:DEGREES_TO_RADIANS(180)
                 clockwise:YES];

    [path closePath];
    [path stroke];
    [path fill];
}

@end

enter image description here

fraggjkee
  • 3,524
  • 2
  • 32
  • 38