iosobjective-cuiviewcore-graphicscompositing

Overlapping transparent UIViews


I’m overlaying two UIViews with a white backgroundColor at 25% opacity. In a small part, they overlap each other, meaning that at that area, they are summed to 50% opacity.

I’d like to keep that 25% opacity, even if the two views overlap, effectively meaning that in those overlapped points, each view’s opacity drops to 12.5% to total 25%.

I’ve done a little looking into compositing but I’m not sure which of these modes would help, or how I’d go about applying them to a specific part of these two UIView instances.

(http://docs.oracle.com/javase/tutorial/2d/advanced/compositing.html is what I was reading, and I found the CGBlendMode for drawing, if it comes to using that (though I’d prefer not to if possible!))


Solution

  • You can't control the compositing mode of views (or, really, CALayers) on iOS.

    The best solution I can think of here is to leave both views with a clearColor (or nil) background, and use a single CAShapeLayer to draw the background of both. If your two views have the same parent, it's not too hard.

    Let's say the parent is of type ParentView. Override layoutSubviews in ParentView to create and update the backdrop layer as necessary. Be sure to send setNeedsLayout to the parent view if you move either of the child views.

    ParentView.h

    #import <UIKit/UIKit.h>
    
    @interface ParentView : UIView
    
    @property (nonatomic, strong) IBOutlet UIView *childView0;
    @property (nonatomic, strong) IBOutlet UIView *childView1;
    
    @end
    

    ParentView.m

    #import "ParentView.h"
    
    @implementation ParentView {
        CAShapeLayer *backdrop;
    }
    
    - (void)layoutSubviews {
        [super layoutSubviews];
        [self layoutBackdrop];
    }
    
    - (void)layoutBackdrop {
        [self createBackdropIfNeeded];
        [self arrangeBackdropBehindChildren];
        [self setBackdropPath];
    }
    
    - (void)createBackdropIfNeeded {
        if (backdrop == nil) {
            backdrop = [CAShapeLayer layer];
            backdrop.fillColor = [UIColor colorWithWhite:1 alpha:0.25].CGColor;
            backdrop.fillRule = kCAFillRuleNonZero;
            backdrop.strokeColor = nil;
        }
    }
    
    - (void)arrangeBackdropBehindChildren {
        [self.layer insertSublayer:backdrop atIndex:0];
    }
    
    - (void)setBackdropPath {
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.childView0.frame];
        [path appendPath:[UIBezierPath bezierPathWithRect:self.childView1.frame]];
        backdrop.path = path.CGPath;
    }
    
    @end