Question

I try to create a flexible frame for in my iPhone app with some small pictures. CustomView inherit UIView and override it's setFrame: method. In setFrame:, I try to call [self setNeedsDisplay]; Every time I scale the photo, this frame really display and changed, but something does not work very well. Code and effect below:

This is the image that I used to draw the frame, in code for self.patternImage


Effect of my code


Effect of my code


//rotate to get mirror image of originImage, and isHorization means the rotation direction - (UIImage*)fetchMirrorImage:(UIImage*)originImage direction:(BOOL)isHorization{ CGSize imageSize = originImage.size; UIGraphicsBeginImageContext(imageSize); CGContextRef context = UIGraphicsGetCurrentContext(); CGAffineTransform transform = CGAffineTransformIdentity;

    if (isHorization) {
        transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
        transform = CGAffineTransformScale(transform, -1.0, 1.0);
    }else {
        transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
        transform = CGAffineTransformScale(transform, 1.0, -1.0);
    }

    CGContextScaleCTM(context, 1, -1);
    CGContextTranslateCTM(context, 0, -imageSize.height);
    CGContextConcatCTM(context, transform);

    CGContextDrawImage(context, CGRectMake(0, 0, imageSize.width, imageSize.height), originImage.CGImage);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

- (UIImage*)fetchPattern:(PatternType)pattern{
    if (!self.patternImage) {
        return nil; 
    }

    UIImage *tmpPattern = nil;
    CGRect fetchRect = CGRectZero;
    CGSize imageSize = self.patternImage.size;
    switch (pattern) {
        case kTopPattern:
            fetchRect = CGRectMake(self.insetSize.width, 0, imageSize.width-self.insetSize.width, self.insetSize.height);
            tmpPattern = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(self.patternImage.CGImage, fetchRect)];
            break;
        case kTopRightPattern:
            fetchRect = CGRectMake(0, 0, self.insetSize.width, self.insetSize.height);
            tmpPattern = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(self.patternImage.CGImage, fetchRect)];

            break;
        case kRightPattern:

            break;
        case kRightBottomPattern:

            break;
        case kBottomPattern:

            break;
        case kLeftBottomPattern:

            break;
        case kLeftPattern:
            fetchRect = CGRectMake(0, self.insetSize.height, self.insetSize.width, imageSize.height-self.insetSize.height);
            tmpPattern = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(self.patternImage.CGImage, fetchRect)];
            break;
        case kLeftTopPattern:
            fetchRect = CGRectMake(0, 0, self.insetSize.width, self.insetSize.height);
            tmpPattern = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(self.patternImage.CGImage, fetchRect)];
            break;
        default:
            break;
    }

    return tmpPattern;
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    if (self.patternImage == nil) {
        return;
    }

    // Drawing code.
    UIImage *conLeftImage = [self fetchPattern:kLeftTopPattern];
    [conLeftImage drawAtPoint:CGPointMake(0, 0)];

    UIImage *topImage = [self fetchPattern:kTopPattern];
    [topImage drawAsPatternInRect:CGRectMake(self.insetSize.width, 0, self.frame.size.width-self.insetSize.width*2, self.insetSize.height)];

    UIImage *leftImage = [self fetchPattern:kLeftPattern];
    [leftImage drawAsPatternInRect:CGRectMake(0, self.insetSize.height, self.insetSize.width, self.frame.size.height-self.insetSize.height*2)];

    UIImage *conRightImage = [self fetchMirrorImage:conLeftImage direction:YES];
    [conRightImage drawAtPoint:CGPointMake(self.frame.size.width-self.insetSize.width, 0)];

    UIImage *rightImage = [self fetchMirrorImage:leftImage direction:YES];
    CGRect rectRight = CGRectMake(self.frame.size.width-self.insetSize.width, self.insetSize.height, self.insetSize.width, self.frame.size.height-self.insetSize.height*2);
    [rightImage drawAsPatternInRect:rectRight];

    UIImage *botRightImage = [self fetchMirrorImage:conRightImage direction:NO];
    [botRightImage drawAtPoint:CGPointMake(self.frame.size.width-self.insetSize.width, self.frame.size.height-self.insetSize.height)];

    UIImage *bottomImage = [self fetchMirrorImage:topImage direction:NO];
    CGRect bottomRect = CGRectMake(self.insetSize.width, self.frame.size.height-self.insetSize.height, self.frame.size.width-self.insetSize.width*2, self.insetSize.height);
    [bottomImage drawAsPatternInRect:bottomRect];

    UIImage *botLeftImage = [self fetchMirrorImage:conLeftImage direction:NO];
    [botLeftImage drawAtPoint:CGPointMake(0, self.frame.size.height-self.insetSize.height)];

    [super drawRect:rect];
}

- (void)setFrame:(CGRect)frame{
    [super setFrame:frame];

    [self setNeedsDisplay];
}
Was it helpful?

Solution

Yeah, I make it!!!final effect The only thing I need to do is set the phase of current context.

CGContextSetPatternPhase(context, CGSizeMake(x, y));

x, y are the start point where I want the pattern to be drawn.

I just made a mistake to use pattern. If I don't set the phase of current context, It start fill the pattern from the left top of context(0,0) every time.

        CGColorRef color = NULL;
    UIImage *leftImage = [UIImage imageNamed:@"like_left.png"];
    color = [UIColor colorWithPatternImage:leftImage].CGColor;
    CGContextSetFillColorWithColor(context, color);
    CGContextFillRect(context, CGRectMake(0, 0, FRAME_WIDTH, rect.size.height));

    UIImage *topImage = [UIImage imageNamed:@"like_top.png"];
    color = [UIColor colorWithPatternImage:topImage].CGColor;
    CGContextSetFillColorWithColor(context, color);
    CGContextFillRect(context, CGRectMake(0, 0, rect.size.width,FRAME_WIDTH));

    UIImage *bottomImage = [UIImage imageNamed:@"like_bom.png"];
    CGContextSetPatternPhase(context, CGSizeMake(0, rect.size.height - FRAME_WIDTH));
    color = [UIColor colorWithPatternImage:bottomImage].CGColor;
    CGContextSetFillColorWithColor(context, color);
    CGContextFillRect(context, CGRectMake(0, rect.size.height - FRAME_WIDTH, rect.size.width, FRAME_WIDTH));

    UIImage *rightImage = [UIImage imageNamed:@"like_right.png"];
    CGContextSetPatternPhase(context, CGSizeMake(rect.size.width - FRAME_WIDTH, 0));
    color = [UIColor colorWithPatternImage:rightImage].CGColor;
    CGContextSetFillColorWithColor(context, color);
    CGContextFillRect(context, CGRectMake(rect.size.width - FRAME_WIDTH, 0, FRAME_WIDTH, rect.size.height));

OTHER TIPS

First, I'll say this:
I don't have a real answer right now. But, I have a hint .. I think it'll be useful.

According to the tutorial posted by Larmarche, you can customize the borders of a UIView easily by overriding drawRect:. And, if you scroll down to the comments below (in the tutorial website), you can see the following comment:

I discovered one other glitch. When you change the RoundRect's bounds, it will not be updated -- so the corners sometimes get all screwy (because they're scaled automatically).

The quick fix is to add the following to both init methods:

self.contentMode = UIViewContentModeRedraw;

Setting the contentMode to Redraw saves you the trouble of calling setNeedsDisplay. You can easily change the frame of the UIView, instantly or animate it, and the UIView will redraw automatically..

I will try to take a better look at your problem later .. I'll try to actually implement it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top