【问题标题】:Mask UIView (UICollectionView) correctly正确屏蔽 UIView (UICollectionView)
【发布时间】:2017-05-12 19:18:12
【问题描述】:

好的,我需要屏蔽一个水平的UICollectionView,使它看起来像一条丝带。我的 web 开发伙伴使用 SVG 完成了它,他将其用作遮罩层。

这是我目前的情况:

这就是我需要的(照片来自我们的网络应用):

除了图像间距的小细节,我有一个像这样的 SVG:

我可以成功转换成UIBezierPath代码(Paintcode):

let pathPath = UIBezierPath()
pathPath.move(to: CGPoint(x: 0.05, y: 0))
pathPath.addCurve(to: CGPoint(x: 162.02, y: 3.8), controlPoint1: CGPoint(x: 0.05, y: 2.1), controlPoint2: CGPoint(x: 72.57, y: 3.8))
pathPath.addCurve(to: CGPoint(x: 324, y: 0), controlPoint1: CGPoint(x: 251.48, y: 3.8), controlPoint2: CGPoint(x: 324, y: 2.1))
pathPath.addLine(to: CGPoint(x: 324, y: 150.2))
pathPath.addCurve(to: CGPoint(x: 162.02, y: 154), controlPoint1: CGPoint(x: 324, y: 152.3), controlPoint2: CGPoint(x: 251.48, y: 154))
pathPath.addCurve(to: CGPoint(x: 0.05, y: 150.2), controlPoint1: CGPoint(x: 72.57, y: 154), controlPoint2: CGPoint(x: 0.05, y: 152.3))
pathPath.addCurve(to: CGPoint(x: 0, y: 0.17), controlPoint1: CGPoint(x: 0.05, y: 148.1), controlPoint2: CGPoint(x: 0, y: 0.17))
pathPath.usesEvenOddFillRule = true
UIColor.lightGray.setFill()
pathPath.fill()

现在我该怎么办?

【问题讨论】:

    标签: ios swift svg uikit uibezierpath


    【解决方案1】:

    您应该使用 UICollectionView 的 mask 属性,将其设置为一个视图,其 alpha 通道指示您想要屏蔽 UICollectionView 的哪一部分。概括起来可能是这样的:

    // If you don't have a custom subclass of UICollectionView... you can handle the resize in the
    // UICollectionViewController or whatever view contoller is handling your view.
    //
    // If you do have a custom subclass of UICollectionView... you could do something similar 
    // in layoutSubviews.
    
    class MyViewController : UICollectionViewController {
    
        // recreate the mask after the view lays out it's subviews
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
    
            if let maskImage = createMaskingImage(size: self.view.bounds.size) {
                let maskView = UIView(frame: self.view.bounds)
                maskView.layer.contents = maskImage
    
                self.view.mask = maskView
            }
        }
    }
    
    // create a masking image for a view of the given size
    func createMaskingImage(size: CGSize) -> UIImage? {
    
        let drawingBounds = CGRect(origin: CGPoint.zero, size: size)
    
        UIGraphicsBeginImageContext(size)
    
        // We're going to jump down to the level of cgContext for scaling
        // You might be able to do this from the level of UIGraphics, but I don't know how so...
        // Implicitly unwrapped optional, but if we can't get a CGContext we're in trouble
    
        let cgContext = UIGraphicsGetCurrentContext()!
        let maskingPath = createMaskingPath()
        let pathBounds = maskingPath.bounds;
    
        cgContext.saveGState()
    
        // Clearing the image may not strictly be necessary
        cgContext.clear(drawingBounds)
    
        // Scale the context so that when we draw the path it fits in the drawing bounds
        // Could just use "size" here instead of drawingBounds.size, but I think this makes it a 
        // little more explicit that we're matching up two rects
        cgContext.scaleBy(x: drawingBounds.size.width / pathBounds.size.width,
                          y: drawingBounds.size.height / pathBounds.size.height)
        cgContext.setFillColor(UIColor.lightGray.cgColor)
        cgContext.addPath(maskingPath.cgPath)
        cgContext.fillPath(using: .evenOdd)
    
        cgContext.restoreGState()
    
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    
        return image
    }
    
    func createMaskingPath() -> UIBezierPath {
        let path = UIBezierPath()
    
        path.move(to: CGPoint(x: 0.05, y: 0))
        path.addCurve(to: CGPoint(x: 162.02, y: 3.8), controlPoint1: CGPoint(x: 0.05, y: 2.1), controlPoint2: CGPoint(x: 72.57, y: 3.8))
        path.addCurve(to: CGPoint(x: 324, y: 0), controlPoint1: CGPoint(x: 251.48, y: 3.8), controlPoint2: CGPoint(x: 324, y: 2.1))
        path.addLine(to: CGPoint(x: 324, y: 150.2))
        path.addCurve(to: CGPoint(x: 162.02, y: 154), controlPoint1: CGPoint(x: 324, y: 152.3), controlPoint2: CGPoint(x: 251.48, y: 154))
        path.addCurve(to: CGPoint(x: 0.05, y: 150.2), controlPoint1: CGPoint(x: 72.57, y: 154), controlPoint2: CGPoint(x: 0.05, y: 152.3))
        path.addCurve(to: CGPoint(x: 0, y: 0.17), controlPoint1: CGPoint(x: 0.05, y: 148.1), controlPoint2: CGPoint(x: 0, y: 0.17))
    
        return path
    }
    

    【讨论】:

    • 谢谢斯科特。你一直在问我的问题,大声笑。您将如何编写此代码以更改大小?我截屏的内容是UITableViewCell。因此,嵌入的UICollectionView 会更改帧。在某处覆盖 layoutIfNeeded() 并在那里绘制?
    • 好的。我编辑了答案以考虑缩放。请注意,我正在缩放 CGPath,这意味着当您缩放更宽或更窄时,功能区的“深度”会发生变化。您可以通过程序更改功能区遮罩形状来解决此问题。
    • 我还应该提到,当我在 Playground 中编译这段代码时,我并没有完全运行它
    【解决方案2】:
    /*
    This is a UICollectionView, which clips normally on the left and right,
    but allows some extra space horizontally.
    A typical example is you need to clip the scrolling items but you
    still need to allow shadows.
    */
    
    import Foundation
    import UIKit
    
    class CustomClipCollectionView: UICollectionView {
    
        private lazy var extraSpaceOnBaseButStillClipSidesNormally: CALayer = {
            let l = CALayer()
            l.backgroundColor = UIColor.black.cgColor
            return l
        }()
    
        override func layoutSubviews() {
            extraSpaceOnBaseButStillClipSidesNormally.frame = bounds.insetBy(
                              dx: 0, dy: -10)
            layer.mask = extraSpaceOnBaseButStillClipSidesNormally
            super.layoutSubviews()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-11
      • 2011-07-24
      • 1970-01-01
      • 2011-03-04
      • 2019-04-14
      • 2011-11-06
      • 2014-09-04
      • 2017-08-22
      相关资源
      最近更新 更多