With the code below I am drawing a rounded rectangle. It draws a nice solid light gray filled rounded rectangle (at the size of \"self\"). I actually want to draw the pixel
Here's yet another approach, using just UI object calls:
- (void)drawRect:(CGRect)rect
{
[[UIColor lightGrayColor] setFill];
CGRect r2 = CGRectInset(rect, 10, 10);
UIBezierPath* p = [UIBezierPath bezierPathWithRoundedRect:r2 cornerRadius:15];
[p appendPath: [UIBezierPath bezierPathWithRect:rect]];
p.usesEvenOddFillRule = YES;
[p fill];
}
Yields this:
The white is the background of the window; the grey is the UIView. As you can see, we're seeing right thru the view to whatever is behind it, which sounds like what you're describing.
Another approach: use UICreateGraphicsContextWithOptions(size, NO, 0)
to make a bitmap. Draw the rectangle into the bitmap. Switch to the erasure blend mode:
CGContextSetBlendMode(con, kCGBlendModeClear);
Now draw the ellipse path and fill it. The result is a rectangle with a transparent elliptical hole. Now close out the image graphics context and draw the image into your original context.
For a drop-in solution:
Add PortholeView.swift
to your Xcode 6 (or higher) project
import UIKit
@IBDesignable class PortholeView: UIView {
@IBInspectable var innerCornerRadius: CGFloat = 10.0
@IBInspectable var inset: CGFloat = 20.0
@IBInspectable var fillColor: UIColor = UIColor.grayColor()
@IBInspectable var strokeWidth: CGFloat = 5.0
@IBInspectable var strokeColor: UIColor = UIColor.blackColor()
override func drawRect(rect: CGRect) {
// Prep constants
let roundRectWidth = rect.width - (2 * inset)
let roundRectHeight = rect.height - (2 * inset)
// Use EvenOdd rule to subtract portalRect from outerFill
// (See http://stackoverflow.com/questions/14141081/uiview-drawrect-draw-the-inverted-pixels-make-a-hole-a-window-negative-space)
let outterFill = UIBezierPath(rect: rect)
let portalRect = CGRectMake(
rect.origin.x + inset,
rect.origin.y + inset,
roundRectWidth,
roundRectHeight)
fillColor.setFill()
let portal = UIBezierPath(roundedRect: portalRect, cornerRadius: innerCornerRadius)
outterFill.appendPath(portal)
outterFill.usesEvenOddFillRule = true
outterFill.fill()
strokeColor.setStroke()
portal.lineWidth = strokeWidth
portal.stroke()
}
}
Bind your target view in Interface Builder
Thanks to @matt for the underlying drawing code & to Apple for exposing IBInspectable/IBDesignable in Interface Builder.
P.S. This venerable Cocoa with Love post will help you understand the "even/odd" rule, and its sibling, the "winding" rule, as well as providing some additional strategies for drawing cutout shapes. http://www.cocoawithlove.com/2010/05/5-ways-to-draw-2d-shape-with-hole-in.html
Add multiple subpaths to your context, and draw with mode kCGPathEOFill
. The Quartz 2D Programming Guide explains in more detail.
// Outer subpath: the whole rect
CGContextAddRect(context, rrect);
// Inner subpath: the area inside the whole rect
CGContextMoveToPoint(context, minx, midy);
...
// Close the inner subpath
CGContextClosePath(context);
// Fill the path
CGContextDrawPath(context, kCGPathEOFill);
I have been looking around for to do this a project I'm working on.
I ended up doing something like this.
I hope this helps someone.
Swift code:
import UIKit
class BarCodeReaderOverlayView: UIView {
@IBOutlet weak var viewFinderView: UIView!
// MARK: - Drawing
override func drawRect(rect: CGRect) {
super.drawRect(rect)
if self.viewFinderView != nil {
// Ensures to use the current background color to set the filling color
self.backgroundColor?.setFill()
UIRectFill(rect)
let layer = CAShapeLayer()
let path = CGPathCreateMutable()
// Make hole in view's overlay
CGPathAddRect(path, nil, self.viewFinderView.frame)
CGPathAddRect(path, nil, bounds)
layer.path = path
layer.fillRule = kCAFillRuleEvenOdd
self.layer.mask = layer
}
}
override func layoutSubviews () {
super.layoutSubviews()
}
// MARK: - Initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
}