Simply Mask a Uiview with a Rectangle

Simply mask a UIView with a rectangle

Thanks to the link from MSK, this is the way I went with which works well:

// Create a mask layer and the frame to determine what will be visible in the view.
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = CGRectMake(0, 0, 50, 100);

// Create a path with the rectangle in it.
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);

// Set the path to the mask layer.
maskLayer.path = path;

// Release the path since it's not covered by ARC.
CGPathRelease(path);

// Set the mask of the view.
viewToMask.layer.mask = maskLayer;

Masking a UIView

#import <QuartzCore/QuartzCore.h>

needed to be added.

Mask UIView with UIImage by Keeping Transparent Area and Removing Coloured Area

Here is one way...

func getImageWithInvertedPixelsOfImage(image: UIImage) -> UIImage {
let rect = CGRect(origin: CGPoint(), size: image.size)

UIGraphicsBeginImageContextWithOptions(image.size, false, 2.0)
UIGraphicsGetCurrentContext()!.setBlendMode(.copy)
image.draw(in: rect)
UIGraphicsGetCurrentContext()!.setBlendMode(.sourceOut)
UIGraphicsGetCurrentContext()!.setFillColor(UIColor.green.cgColor)
UIGraphicsGetCurrentContext()!.fill(rect)

let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
}

// I named your original image "WaveMask"
let iWave = UIImage(named: "WaveMask")
let invWave = getImageWithInvertedPixelsOfImage(image: iWave!)

// invWave is now Green where it was alpha, and alpha where it was gray
// and can now be used as a mask

How to mask and add shadow to a UIView

Thanks @WilsonXJ
I changed mask to addSubLayer.

This is the answer that worked for me:

    let p = UIBezierPath()
p.moveToPoint(CGPointMake(20, 20))
p.addLineToPoint(CGPointMake(100, 20))
p.addLineToPoint(CGPointMake(100, 50))
p.addLineToPoint(CGPointMake(110, 55))
p.addLineToPoint(CGPointMake(100, 60))
p.addLineToPoint(CGPointMake(100, 100))
p.addLineToPoint(CGPointMake(20, 100))
p.closePath()

let s = CAShapeLayer()
s.fillColor = UIColor.whiteColor().CGColor
s.frame = layer.bounds
s.path = p.CGPath

layer.backgroundColor = UIColor.clearColor().CGColor
layer.addSublayer(s)

layer.masksToBounds = true
layer.shadowColor = UIColor.yellowColor().CGColor
layer.shadowOffset = CGSizeZero
layer.shadowOpacity = 0.9
layer.shadowPath = p.CGPath
layer.shadowRadius = 10

Using the existing paths to create a mask to a UIView?

How about instantiating a new UIView and using the path you just drew as a clipping mask? Then add the newly clipped object as a subview to your UIButton.

Adding a partial mask over an UIImageView

You can use a CAGradientLayer as a mask:

    gLayer.startPoint = CGPoint.zero
gLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
gLayer.locations = [
0.0, 0.5, 0.5, 1.0,
]
gLayer.colors = [
UIColor.black.cgColor,
UIColor.black.cgColor,
UIColor.black.withAlphaComponent(0.5).cgColor,
UIColor.black.withAlphaComponent(0.5).cgColor,
]

This would create a horizontal gradient, with the left half full alpha and the right half 50% alpha.

So, a white view with this as a mask would look like this:

Sample Image

If we set the image to your star, it looks like this:

Sample Image

If we want the star to be "75% filled" we change the locations:

    gLayer.locations = [
0.0, 0.75, 0.75, 1.0,
]

resulting in:

Sample Image

Here is an example implementation for a "Five Star" rating view:

@IBDesignable
class FiveStarRatingView: UIView {

@IBInspectable
public var rating: CGFloat = 0.0 {
didSet {
var r = rating
stack.arrangedSubviews.forEach {
if let v = $0 as? PercentImageView {
v.percent = min(1.0, r)
r -= 1.0
}
}
}
}

@IBInspectable
public var ratingImage: UIImage = UIImage() {
didSet {
stack.arrangedSubviews.forEach {
if let v = $0 as? PercentImageView {
v.image = ratingImage
}
}
}
}

@IBInspectable
public var tranparency: CGFloat = 0.5 {
didSet {
stack.arrangedSubviews.forEach {
if let v = $0 as? PercentImageView {
v.tranparency = tranparency
}
}
}
}

override var intrinsicContentSize: CGSize {
return CGSize(width: 100.0, height: 20.0)
}

private let stack: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.alignment = .center
v.distribution = .fillEqually
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()

override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
addSubview(stack)
// constrain stack view to all 4 sides
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: topAnchor),
stack.leadingAnchor.constraint(equalTo: leadingAnchor),
stack.trailingAnchor.constraint(equalTo: trailingAnchor),
stack.bottomAnchor.constraint(equalTo: bottomAnchor),
])
// add 5 Percent Image Views to the stack view
for _ in 1...5 {
let v = PercentImageView(frame: .zero)
stack.addArrangedSubview(v)
v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
}
}

private class PercentImageView: UIImageView {

var percent: CGFloat = 0.0 {
didSet {
setNeedsLayout()
}
}

var tranparency: CGFloat = 0.5 {
didSet {
setNeedsLayout()
}
}

private let gLayer = CAGradientLayer()

override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
gLayer.startPoint = CGPoint.zero
gLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
layer.mask = gLayer
}
override func layoutSubviews() {
super.layoutSubviews()

// we don't want the layer's intrinsic animation
CATransaction.begin()
CATransaction.setDisableActions(true)

gLayer.frame = bounds
gLayer.locations = [
0.0, percent as NSNumber, percent as NSNumber, 1.0,
]
gLayer.colors = [
UIColor.black.cgColor,
UIColor.black.cgColor,
UIColor.black.withAlphaComponent(tranparency).cgColor,
UIColor.black.withAlphaComponent(tranparency).cgColor,
]

CATransaction.commit()
}
}

}

class StarRatingViewController: UIViewController {

let ratingView = FiveStarRatingView()

let slider = UISlider()
let valueLabel = UILabel()

override func viewDidLoad() {
super.viewDidLoad()

guard let starImage = UIImage(named: "star") else {
fatalError("Could not load image named \"star\"")
}

// add a slider and a couple labels so we can change the rating
let minLabel = UILabel()
let maxLabel = UILabel()
[slider, valueLabel, minLabel, maxLabel].forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
if let v = $0 as? UILabel {
v.textAlignment = .center
}
}

let g = view.safeAreaLayoutGuide

NSLayoutConstraint.activate([
valueLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
valueLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),

slider.topAnchor.constraint(equalTo: valueLabel.bottomAnchor, constant: 8.0),
slider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 32.0),
slider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -32.0),

minLabel.topAnchor.constraint(equalTo: slider.bottomAnchor, constant: 8.0),
minLabel.centerXAnchor.constraint(equalTo: slider.leadingAnchor, constant: 0.0),

maxLabel.topAnchor.constraint(equalTo: slider.bottomAnchor, constant: 8.0),
maxLabel.centerXAnchor.constraint(equalTo: slider.trailingAnchor, constant: 0.0),
])
minLabel.text = "0"
maxLabel.text = "5"

ratingView.translatesAutoresizingMaskIntoConstraints = false

view.addSubview(ratingView)

NSLayoutConstraint.activate([
// constrain the rating view centered in the view
// 300-pts wide
// height will be auto-set by the rating view
ratingView.topAnchor.constraint(equalTo: minLabel.bottomAnchor, constant: 20.0),
ratingView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
ratingView.widthAnchor.constraint(equalToConstant: 240.0),
])

// use the star image
ratingView.ratingImage = starImage

// start at rating of 0 stars
updateValue(0.0)
slider.value = 0

slider.addTarget(self, action: #selector(self.sliderChanged(_:)), for: .valueChanged)
}

@objc func sliderChanged(_ sender: UISlider) {
// round the slider value to 2 decimal places
updateValue((sender.value * 5.0).rounded(digits: 2))
}

func updateValue(_ v: Float) -> Void {
valueLabel.text = String(format: "%.2f", v)
ratingView.rating = CGFloat(v)
}

}

extension Float {
func rounded(digits: Int) -> Float {
let multiplier = Float(pow(10.0, Double(digits)))
return (self * multiplier).rounded() / multiplier
}
}

Result:

Sample Image

Note that the FiveStarRatingView class is marked @IBDesignable so you can add it in Storyboard / IB and set image, amount of transparency and rating at design-time.



Related Topics



Leave a reply



Submit