Masking UIView/UIImageView to cutout transparent t

2019-04-06 18:58发布

How can I mask an UIView or UIImageView so that a text is cutout from it?

I googled a lot and it seems that many people struggled the same. Most irritating I always tried to invert the alpha of a snapshotted view to get the result.

What I want looks like this:

enter image description here

2条回答
三岁会撩人
2楼-- · 2019-04-06 19:29

This is the custom mask label.

import UIKit
final class MaskLabel: UILabel {

// MARK: - IBInspectoable
@IBInspectable var cornerRadius: CGFloat {
    get { return self.layer.cornerRadius }
    set { self.layer.cornerRadius = newValue }
}

@IBInspectable var borderWidth: CGFloat {
    get { return self.layer.cornerRadius }
    set { self.layer.borderWidth = newValue }
}

@IBInspectable var borderColor: UIColor {
    get { return UIColor(cgColor: self.layer.borderColor ?? UIColor.clear.cgColor) }
    set { self.layer.borderColor = newValue.cgColor }
}

@IBInspectable var insetTop: CGFloat {
    get { return self.textInsets.top }
    set { self.textInsets.top = newValue }
}

@IBInspectable var insetLeft: CGFloat {
    get { return self.textInsets.left }
    set { self.textInsets.left = newValue }
}

@IBInspectable var insetBottom: CGFloat {
    get { return self.textInsets.bottom }
    set { self.textInsets.bottom = newValue }
}

@IBInspectable var insetRight: CGFloat {
    get { return self.textInsets.right }
    set { self.textInsets.right = newValue }
}



// MARK: - Value
// MARK: Public
private var textInsets = UIEdgeInsets.zero
private var originalBackgroundColor: UIColor? = nil



// MARK: - Initializer
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    setLabelUI()
}

override init(frame: CGRect) {
    super.init(frame: frame)

    setLabelUI()
}

override func prepareForInterfaceBuilder() {
    setLabelUI()
}


// MARK: - Draw
override func drawText(in rect: CGRect) {
    super.drawText(in: UIEdgeInsetsInsetRect(rect, textInsets))
    guard let context = UIGraphicsGetCurrentContext() else { return }

    context.saveGState()
    context.setBlendMode(.clear)

    originalBackgroundColor?.setFill()
    UIRectFill(rect)

    super.drawText(in: rect)
    context.restoreGState()
}


// MARK: - Function
// MARK: Private
private func setLabelUI() {
    // cache (Before masking the label, the background color must be clear. So we have to cache it)
    originalBackgroundColor = backgroundColor
    backgroundColor = .clear

    layer.cornerRadius = cornerRadius
    layer.borderWidth  = borderWidth
    layer.borderColor  = borderColor.cgColor
}
}

enter image description here enter image description here

This is the result. enter image description here

查看更多
别忘想泡老子
3楼-- · 2019-04-06 19:47

The main problem I had was my understanding. Instead of taking a colored view and trying to make a transparent hole in it, we can just layer it the other way around. So we have the colored background in the back, followed by the image in front that has the mask on it to only show the text part. And actually, that's pretty simple if you're using iOS 8+ by using the maskView property of UIView.

So it could look something like this in swift:

    let coloredBackground = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
    coloredBackground.backgroundColor = UIColor.greenColor()

    let imageView = UIImageView(frame: coloredBackground.bounds)
    imageView.image = UIImage(named: "myImage")
    coloredBackground.addSubview(imageView)

    let label = UILabel(frame: coloredBackground.bounds)
    label.text = "stackoverflow"
    coloredBackground.addSubview(label)

    imageView.maskView = label
查看更多
登录 后发表回答