UIView drawRect:绘制倒像素,制作一个洞,一个窗口,负空间

使用下面的代码,我绘制一个圆角矩形。 它绘制了一个漂亮的浅灰色填充圆角矩形(大小为“self”)。 我实际上想绘制这个像素的倒数,即:不是一个实心的圆角矩形,而是一个实心的浅灰色矩形中这个圆形矩形形状的窗口或孔。

我需要使用反向剪辑方法吗? 或者我需要使用bezier路径? 如果这是非常基本的借口,虽然找不到信息。

谢谢阅读!

- (void)drawRect:(CGRect)rect { // get the context CGContextRef context = UIGraphicsGetCurrentContext CGContextSaveGState(context); //draw the rounded rectangle CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]); CGContextSetRGBFillColor(context, 0.8, 0.8, 0.8, 1.0); CGContextSetLineWidth(context, _lineWidth); CGRect rrect = CGRectMake(CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetWidth(rect), CGRectGetHeight(rect)); CGFloat radius = _cornerRadius; CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect), maxx = CGRectGetMaxX(rrect); CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect), maxy = CGRectGetMaxY(rrect); CGContextMoveToPoint(context, minx, midy); // Add an arc through 2 to 3 CGContextAddArcToPoint(context, minx, miny, midx, miny, radius); // Add an arc through 4 to 5 CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius); // Add an arc through 6 to 7 CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius); // Add an arc through 8 to 9 CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius); // Close the path CGContextClosePath(context); // Fill the path CGContextDrawPath(context, kCGPathFill); CGContextRestoreGState(context); } 

将多个子路径添加到上下文中,并使用模式kCGPathEOFill绘制。 Quartz 2D Programming Guide更详细地解释了这一点。

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

这是另一种方法,只使用UI对象调用:

 - (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]; } 

收益率:

在此处输入图像描述

白色是窗户的背景; 灰色是UIView。 正如你所看到的,我们正在通过视图看到它背后的任何东西,听起来就像你所描述的那样。

对于插入式解决方案:

  1. PortholeView.swift添加到您的Xcode 6(或更高版本)项目中

     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() } } 
  2. 在Interface Builder中绑定目标视图

在此处输入图像描述

  1. 在IB中调整插图,外部填充颜色,笔触颜色和笔触宽度!

在此处输入图像描述

  1. 设置你的约束和其他视图,记住你可能必须修改PortholeView,如果你需要你的矩形以特殊的方式进行缩放以及诸如此类的东西。 在这种情况下,我在PortholeView后面有一个UIImage来演示圆形rect如何通过“甚至奇怪的规则”“切断”周围路径。

在此处输入图像描述

感谢@matt提供底层绘图代码,感谢Apple在Interface Builder中公开IBInspectable / IBDesignable。

PS这个令人尊敬的Cocoa with Lovepost将帮助您理解“偶数/奇数”规则及其兄弟,“缠绕”规则,以及提供一些额外的策略来绘制剪切形状。 http://www.cocoawithlove.com/2010/05/5-ways-to-draw-2d-shape-with-hole-in.html

另一种方法:使用UICreateGraphicsContextWithOptions(size, NO, 0)来制作位图。 将矩形绘制到位图中。 切换到擦除混合模式:

 CGContextSetBlendMode(con, kCGBlendModeClear); 

现在绘制椭圆路径并填充它。 结果是一个带有透明椭圆孔的矩形。 现在关闭图像图形上下文并将图像绘制到原始上下文中。

我一直在寻找这个我正在做的项目。

我最终做了这样的事情。

在此处输入图像描述

我希望这可以帮助别人。

SWIFT代码:

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