【问题标题】:UIView drawRect: Draw the inverted pixels, make a hole, a window, negative spaceUIView drawRect:绘制倒置像素,打个洞,一个窗口,负空间
【发布时间】:2012-12-17 22:38:41
【问题描述】:

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

我需要使用反向剪辑方法吗?还是我需要使用贝塞尔路径?对不起,如果这是非常基本的,但找不到信息。

感谢阅读!

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

}

【问题讨论】:

    标签: ios core-graphics drawrect


    【解决方案1】:

    我一直在寻找我正在从事的项目。

    我最终做了这样的事情。

    我希望这对某人有所帮助。

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

    【讨论】:

    【解决方案2】:

    对于插入式解决方案:

    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 来演示圆形矩形如何通过“奇偶规则”从周围的路径中“切出”。

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

    附:这篇著名的Cocoa with Love 帖子将帮助您理解“偶数/奇数”规则及其兄弟“缠绕”规则,并提供一些用于绘制剪切形状的额外策略。 http://www.cocoawithlove.com/2010/05/5-ways-to-draw-2d-shape-with-hole-in.html

    【讨论】:

      【解决方案3】:

      这是另一种方法,只使用 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。正如你所看到的,我们可以通过视图看到它背后的任何东西,这听起来就像你所描述的那样。

      【讨论】:

      • 好东西。感谢您的回答。
      • @matt 一如既往的伟大而简单的答案。非常感谢!
      • 我不懂方法。看起来 p 是内部的圆角矩形。如果是这样,你为什么要填写 p?你不应该填充灰色矩形吗?如果您有第二个需要看穿的矩形,您将如何扩展它?
      • @Cindeselia "看起来 p 是内部圆角矩形" 不,p 是由外部矩形和内部圆角矩形组成的复合路径。而且我使用了奇偶规则,所以我要填充的是它们之间的空间。
      【解决方案4】:

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

      CGContextSetBlendMode(con, kCGBlendModeClear);
      

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

      【讨论】:

      • 这会起作用,但会比一开始填充正确的路径慢很多。此外,如果您想打印或导出为 PDF,质量不会很好,而且您的 PDF 会比需要的大很多。
      【解决方案5】:

      为您的上下文添加多个子路径,并使用模式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);
      

      【讨论】:

        猜你喜欢
        • 2023-03-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-09
        • 2011-02-28
        • 1970-01-01
        • 1970-01-01
        • 2010-10-20
        相关资源
        最近更新 更多