How to set corner radius to a collection of UIButtons - swift

I'm new at using Xcode. My question is regarding "How to set corner.Radius for UIButtons (collection) instead of doing 1 by 1. Once the collection is created i'm using the following line:
self.myButtons.layer.cornerRadius = 10
but that is for a single button. Is it possible to do this for a "collection" of buttons?
enter image description here
any help is greatly appreciated.
#IBOutlet var myButtons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.myButtons.layer.conerRadius = 10

A few options:
Iterate through them yourself:
myButtons.forEach { $0.layer.cornerRadius = 10 }
Use NSArray and its setValue(_:forKey:)
(myButtons as NSArray).setValue(10, forKey: "cornerRadius")
I’d lean towards the former, but the latter is the old Objective-C way of doing it (which is why we had to bridge to NSArray).
The other approach is to define your own UIButton subclass, e.g. RoundedButton that does this for you. Just set the base class for your button in IB to be your custom subclass.
E.g. for fixed corner radius (which you can also adjust right in IB):
#IBDesignable
class RoundedButton: UIButton {
#IBInspectable var cornerRadius: CGFloat = 10 {
didSet {
layer.cornerRadius = cornerRadius
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
}
private extension RoundedButton {
func configure() {
layer.cornerRadius = cornerRadius
}
}
Or, if you want dynamic rounding based upon the height:
#IBDesignable
class RoundedButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
let radius = min(bounds.width, bounds.height) / 2
layer.cornerRadius = radius
}
}
The virtue of this approach is that you can see your rounded buttons rendered right in IB.

Follow this below steps -
1.Choose UIButton from the object library
2.Drag to your storyboard
3.Choose border style as none.
4.Create a Swift file and add this below extension -
extension UIView {
#IBInspectable var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
#IBInspectable var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
}
}
#IBInspectable var borderColor: UIColor? {
get {
return UIColor(cgColor: layer.borderColor!)
}
set {
layer.borderColor = newValue?.cgColor
}
}
}
extension UIButton {
func roundedButton(){
let maskPAth1 = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.topLeft , .topRight],
cornerRadii:CGSize(width:8.0, height:8.0))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = self.bounds
maskLayer1.path = maskPAth1.cgPath
self.layer.mask = maskLayer1
}
}
extension UITextField {
func setLeftPaddingPoints(_ amount:CGFloat){
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: self.frame.size.height))
self.leftView = paddingView
self.leftViewMode = .always
}
func setRightPaddingPoints(_ amount:CGFloat) {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: self.frame.size.height))
self.rightView = paddingView
self.rightViewMode = .always
}
}
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
#objc func checkMaxLength(textField: UITextField) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
5.Now you can access this extensions either from storyboard or from code to change the values and see the effects.
6.You can change the corner radius, border width, border colour for UIView,UIButton,UITexfield.Try this Method.
Hope this method also helps.

/// Other way to set Corner radius of view; also inspectable from Storyboard.
public extension UIView {
#IBInspectable public var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
// layer.masksToBounds = true
layer.cornerRadius = abs(CGFloat(Int(newValue * 100)) / 100)
}
}}
By usisng this you can set border radius from storyboard
OR
for btn in yourbuttonCollectionName {btn.cornerRadius = 10.0}

You can do this in interface builder. Select your button and then tap the "Identity Inspector" (3rd tab on the right). Under "User Defined Runtime Attributes" hit the plus button. and type layer.conerRadius for the key and 1 for the value. This will set the corner radius by KVO. you can now copy this button or duplicate it with alt+drag and the copies will also have the corner radius (note it doesn't show in the preview window, but it will show at run time).
Alternatively in code:
myButtons.forEach { button in
button.layer.cornerRadius = 1
}
If you are using an outlet collection you can do this in a didSet since that will be called when iOS sets the outlet from the nib file/ storyboard.

extension UIView {
func addCornerRadius(_ radius: CGFloat) {
self.layer.cornerRadius = radius
}
func applyBorder(_ width: CGFloat, borderColor: UIColor) {
self.layer.borderWidth = width
self.layer.borderColor = borderColor.cgColor
}
func addShadow(color: UIColor, opacity: Float, offset: CGSize, radius: CGFloat) {
self.layer.shadowColor = color.cgColor
self.layer.shadowOpacity = opacity
self.layer.shadowOffset = offset
self.layer.shadowRadius = radius
}
func displayToast(message: String) {
let style = CSToastStyle(defaultStyle: ())
style?.backgroundColor = UIColor.black
style?.titleColor = UIColor.white
style?.messageColor = UIColor.white
makeToast(message, duration: 3.0, position: CSToastPositionTop, style: style)
} }
Use As Below :
self.view.addCornerRadius(10)
self.view.addShadow(color: .lightGray, opacity: 1.0, offset: CGSize(width: 1, height: 1), radius: 2)

Related

Is there a way to set border to round rectangle that is added to CGMutablePath in Swift?

I am trying to create an overlay view, that has a "cutout" part that comes from a frame that is passed to the view, that size and position of that passed frame will change upon creation of the view. And in that "cutout" part I am expecting to see the content that is under that overlay view. Tried to set border to a rounded rectangle that is added to a CGMutablePath, but no luck.
The expected result is something like this:
The code I currently have in my UIView class, without tried solutions as I can't seem to get them to work properly. This current code displays the expected result, but without the red border:
override func draw(_ rect: CGRect) {
super.draw(rect)
UIColor.blue.setFill()
UIRectFill(rect)
let shapeLayer = CAShapeLayer()
let path = CGMutablePath()
// frame that will change position on the screen
if let frame = changingFrame {
path.addRoundedRect(in: frame, cornerWidth: 16, cornerHeight: 16)
}
path.addRect(bounds)
shapeLayer.path = path
shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd
self.layer.mask = shapeLayer
}
I have tried solutions from here, here, but no luck as CAShapeLayer for border just overlays existing one.
What can I do differently to achieve the expected result? Thanks!
try this ⭐️
If all you want to do is create a rounded rectangle, then you can simply use.
let rectangle = CGRect(x: 0, y: 0, width: 100, height: 100)
let path = UIBezierPath(roundedRect: rectangle, cornerRadius: 20)
view.clipsToBounds = true
view.layer.cornerRadius = 10.0
let border = CAShapeLayer()
border.path = UIBezierPath(roundedRect:view.bounds, cornerRadius:10.0).cgPath
border.frame = view.bounds
border.fillColor = nil
border.strokeColor = UIColor.purple.cgColor
border.lineWidth = borderWidth * 2.0 // doubled since half will be clipped
border.lineDashPattern = [1.0]
view.layer.addSublayer(border)
One approach is to use two sublayers... a "cutout" layer and a "border" layer.
Use the same path for the cutout and the border shape, setting the line width and stroke color for the "outline".
Here's an example -- including making it #IBDesignable with a few #IBInspectable properties:
#IBDesignable
class BorderedCutoutView: UIView {
#IBInspectable
var bkgColor: UIColor = .systemBlue {
didSet {
setNeedsLayout()
}
}
#IBInspectable
var brdColor: UIColor = .white {
didSet {
setNeedsLayout()
}
}
#IBInspectable
var brdWidth: CGFloat = 1 {
didSet {
setNeedsLayout()
}
}
#IBInspectable
var radius: CGFloat = 20 {
didSet {
setNeedsLayout()
}
}
#IBInspectable
var horizInset: CGFloat = 40.0 {
didSet {
setNeedsLayout()
}
}
#IBInspectable
var vertInset: CGFloat = 60.0 {
didSet {
setNeedsLayout()
}
}
private let cutoutLayer = CAShapeLayer()
private let borderLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
backgroundColor = .clear
}
private func commonInit() -> Void {
backgroundColor = .clear
layer.addSublayer(cutoutLayer)
layer.addSublayer(borderLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(rect: bounds)
let cp = UIBezierPath(roundedRect: bounds.insetBy(dx: horizInset, dy: vertInset), cornerRadius: radius)
path.append(cp)
path.usesEvenOddFillRule = true
cutoutLayer.path = path.cgPath
cutoutLayer.fillRule = .evenOdd
cutoutLayer.fillColor = bkgColor.cgColor
borderLayer.path = cp.cgPath
borderLayer.fillColor = UIColor.clear.cgColor
borderLayer.lineWidth = brdWidth
borderLayer.strokeColor = brdColor.cgColor
}
}
This example uses horizontal and vertical "inset" values to center the cutout in the view.
Result:

UIButton System style selected state, keep image and background

When using the System style of UIButton (nope, i don't want to use the Custom style, as the system provides animations, etc...)
The selected state of system button adds a background and removes the image
This is the Default state
And i want to achieve a selected style like this, where the look when selected is the same as the custom button
ok, finally manged that one out, the key was not to allow the switch to selected state
class ControlButton: UIButton {
var sImage: UIImage?
var dImage: UIImage?
override func awakeFromNib() {
super.awakeFromNib()
sImage = image(for: .selected)
dImage = image(for: .normal)
}
override open var isSelected: Bool {
set {
if newValue {
setImage(sImage, for: .normal)
} else {
setImage(dImage, for: .normal)
}
}
get {
return false
}
}
}
Add this class and set it to your button class
class KButton: UIButton {
var view: UIButton!
#IBInspectable public var textPadding: CGFloat = 5.0 {
didSet {
layoutSubviews()
}
}
#IBInspectable public var circleRadius: CGFloat = 10 {
didSet {
layoutSubviews()
}
}
#IBInspectable public var circleWidth: CGFloat = 2.0 {
didSet {
layoutSubviews()
}
}
#IBInspectable public var currentState: Bool = false {
didSet {
layoutSubviews()
}
}
override func layoutSubviews() {
super.layoutSubviews()
if view != nil {
view?.removeFromSuperview()
}
view = UIButton(frame: CGRect(x: -(self.frame.height) - textPadding, y: 0, width: self.frame.height, height: self.frame.height))
view.backgroundColor = UIColor.clear
let circlePath = UIBezierPath(arcCenter: CGPoint(x: view.frame.height/2, y: view.frame.height/2), radius: circleRadius, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = self.tintColor.cgColor
shapeLayer.lineWidth = circleWidth
view.layer.addSublayer(shapeLayer)
if currentState {
let circlePath1 = UIBezierPath(arcCenter: CGPoint(x: view.frame.height/2, y: view.frame.height/2), radius: (circleRadius - (circleWidth * 2)), startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
let shapeLayer1 = CAShapeLayer()
shapeLayer1.path = circlePath1.cgPath
shapeLayer1.fillColor = self.tintColor.cgColor
shapeLayer1.strokeColor = self.tintColor.cgColor
shapeLayer1.lineWidth = circleWidth
view.layer.addSublayer(shapeLayer1)
}
self.addSubview(view)
}
}
Then in you click action
#IBAction func buttonClicked(_ sender: KButton) {
sender.currentState = !sender.currentState
}
Make sure to choose the type as KButton

Swift - How to create this form bubble effect

So I am trying to create the following effect using xcode swift.
I am trying to re-create the form bubble which contains the text fields and the text fields themselves, in the following style.
What I have tried so far:
Creating a UI View with curved borders, then using a transparent textfield and UILabels to indicate the field content.
I assume that the that must be a UItableview given the indent line but I am not sure how to style the tableview the same way. I assumed I can use the layer.cornerRadius as I did for the UIView but this doesn't seem to work.
Also is the entire view controller a UItableview controller or UICollectionView?
Any help on how to create the form as below would be appreciated.
In iOS 13 you can simply set the Style to Inset Grouped and the UITableView looks exactly like that - no additional changes necessary.
Using layer.cornerRadius for the whole tableview will not work. You should use a UITableviewController or a normal UIViewController which contains a tableView then divide your tableview into 3 sections
First section should be profile: no doubt about this section, border your cell the be the same as design
Second section should be personal information. 3 cells, each cell contain the text field. You can calculate and create border for the cell by corresponding position (top cell will be bordered top left & right, bottom cell will be bordered bottom left & right, otherwise no bordered)
Third section: no doubt about this, just one cell (switch account)
SWIFT Solution
Thanks to Paulo Silva
Step 1:
You can simply create a swift file named it as 'CUIView' (or your own wish) and use following IBDesignable code inside it and save .
//
// CUIView.swift
// CustomUIView
//
// Created by Paulo Silva on 23/08/2019.
// Copyright © 2019 example. All rights reserved.
//
import UIKit
import UIKit
import CoreGraphics
#IBDesignable class CUIView: UIView {
// MARK: - Private Variables -
private let containerView = UIView()
private var containerImageView = UIImageView()
// MARK: - Public Attributes -
#IBInspectable public var backgroundImage: UIImage? {
get {
return self.containerImageView.image
}
set {
// addShadowColorFromBackgroundImage()
self.containerImageView.image = newValue
}
}
override open var backgroundColor: UIColor? {
didSet(new) {
if let color = new {
containerView.backgroundColor = color
}
if backgroundColor != UIColor.clear { backgroundColor = UIColor.clear }
}
}
#IBInspectable var borderColor: UIColor {
get {
return UIColor(cgColor: self.containerView.layer.borderColor!)
}
set {
self.layer.borderColor = newValue.cgColor
self.containerView.layer.borderColor = newValue.cgColor
}
}
#IBInspectable var borderWidth: CGFloat {
get {
return self.containerView.layer.borderWidth
}
set {
self.layer.borderWidth = newValue
self.containerView.layer.borderWidth = newValue
}
}
#IBInspectable var cornerRadius: CGFloat {
get {
return self.containerView.layer.cornerRadius
}
set {
self.layer.cornerRadius = newValue
self.containerView.layer.cornerRadius = newValue
}
}
#IBInspectable var shadowOpacity: Float {
get {
return self.layer.shadowOpacity
}
set {
self.layer.shadowOpacity = newValue
}
}
#IBInspectable var shadowRadius: CGFloat {
get {
return self.layer.shadowRadius
}
set {
self.layer.shadowRadius = newValue
}
}
#IBInspectable var shadowOffset: CGSize {
get {
return self.layer.shadowOffset
}
set {
self.layer.shadowOffset = newValue
}
}
#IBInspectable var shadowColor: UIColor {
get {
return UIColor(cgColor: self.layer.shadowColor!)
}
set {
self.layer.shadowColor = newValue.cgColor
}
}
// #IBInspectable var shadowColorFormImage: Bool = false {
// didSet {
// addShadowColorFromBackgroundImage()
// }
// }
// MARK: - Life Cycle -
override init(frame: CGRect) {
super.init(frame: frame)
addViewLayoutSubViews()
refreshViewLayout()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addViewLayoutSubViews()
refreshViewLayout()
}
override open func draw(_ rect: CGRect) {
super.draw(rect)
refreshViewLayout()
// addShadowColorFromBackgroundImage()
}
override func layoutSubviews() {
super.layoutSubviews()
refreshViewLayout()
// addShadowColorFromBackgroundImage()
}
// MARK: - Private Methods -
private func refreshViewLayout() {
// View
self.clipsToBounds = true
self.layer.masksToBounds = false
self.layer.cornerRadius = cornerRadius
// Shadow
self.layer.shadowOpacity = shadowOpacity
self.layer.shadowColor = shadowColor.cgColor
self.layer.shadowOffset = shadowOffset
self.layer.shadowRadius = shadowRadius
// Container View
self.containerView.layer.masksToBounds = true
self.containerView.layer.cornerRadius = cornerRadius
// Image View
self.containerImageView.backgroundColor = UIColor.clear
self.containerImageView.image = backgroundImage
self.containerImageView.layer.cornerRadius = cornerRadius
self.containerImageView.layer.masksToBounds = true
self.containerImageView.clipsToBounds = true
self.containerImageView.contentMode = .redraw
}
private func addViewLayoutSubViews() {
// add subViews
self.addSubview(self.containerView)
self.containerView.addSubview(self.containerImageView)
// add image constraints
self.containerImageView.translatesAutoresizingMaskIntoConstraints = false
self.containerImageView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
self.containerImageView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
self.containerImageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
self.containerImageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
// add view constraints
self.containerView.translatesAutoresizingMaskIntoConstraints = false
self.containerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
self.containerView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
self.containerView.topAnchor.constraint(equalTo: topAnchor).isActive = true
self.containerView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
// private func addShadowColorFromBackgroundImage() {
// // Get the averageColor from the image for set the Shadow Color
// if shadowColorFormImage {
// let week = self
// DispatchQueue.main.async {
// week.shadowColor = (week.containerImageView.image?.averageColor)!
// }
// }
// }
}
extension UIImage {
static func imageWithColor(tintColor: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
tintColor.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
func withBackground(color: UIColor, opaque: Bool = true) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
guard let ctx = UIGraphicsGetCurrentContext() else { return self }
defer { UIGraphicsEndImageContext() }
let rect = CGRect(origin: .zero, size: size)
ctx.setFillColor(color.cgColor)
ctx.fill(rect)
ctx.concatenate(CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height))
ctx.draw(cgImage!, in: rect)
return UIGraphicsGetImageFromCurrentImageContext() ?? self
}
var averageColor: UIColor? {
guard let inputImage = CIImage(image: self) else { return nil }
let extentVector = CIVector(x: inputImage.extent.origin.x, y: inputImage.extent.origin.y, z: inputImage.extent.size.width, w: inputImage.extent.size.height)
guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: inputImage, kCIInputExtentKey: extentVector]) else { return nil }
guard let outputImage = filter.outputImage else { return nil }
var bitmap = [UInt8](repeating: 0, count: 4)
let context = CIContext(options: [.workingColorSpace: kCFNull as Any])
context.render(outputImage, toBitmap: &bitmap, rowBytes: 4, bounds: CGRect(x: 0, y: 0, width: 1, height: 1), format: .RGBA8, colorSpace: nil)
return UIColor(red: CGFloat(bitmap[0]) / 255, green: CGFloat(bitmap[1]) / 255, blue: CGFloat(bitmap[2]) / 255, alpha: CGFloat(bitmap[3]) / 255)
}
}
extension NSLayoutConstraint {
func constraintWithMultiplier(_ multiplier: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint(item: self.firstItem!, attribute: self.firstAttribute, relatedBy: self.relation, toItem: self.secondItem, attribute: self.secondAttribute, multiplier: multiplier, constant: self.constant)
}
}
extension UIScreen {
enum SizeType: CGFloat {
case unknown = 0.0
case iPhone4 = 960.0
case iPhone5 = 1136.0
case iPhone6 = 1334.0
case iPhone6Plus = 1920.0
}
var sizeType: SizeType {
let height = nativeBounds.height
guard let sizeType = SizeType(rawValue: height) else { return .unknown }
return sizeType
}
}
Step 2 : Connect your view associated class as 'CUIView' as following image
Step 3: Give corner radius and set background as like as following image .
For all the group textField and textfield you can use a view as parents view and associated class to get this outcome . For underline you can use simple view with minimum height to get desired design .

Make the UIImage to the rounded form? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I tried the bellow code then I got the result like above image
Expected result for Image view is shown bellow
I have posted the image of expectated result.
I have dragged the UIImage view and I want to make the image view that display in round format.
So please guide me out.
#IBOutlet weak var imgView : UIImageView!
imgView.layer.borderWidth = 1
imgView.layer.masksToBounds = true
imgView.layer.borderColor = UIColor.black.cgColor
imgView.layer.cornerRadius = imgView.frame.width/2
imgView.clipsToBounds = true
//I tried this still not getting the answer
UIImageView is subclass of UIView. Each UIView has its own CALayer. You can set the border color, border width and corner radius of the view by using its layer. To show the UIImageView in circular shape, first height and weight of the view should be equal and then set corner radius as height/2 or width/2.
If myImage is UIImageView for which height and weight are equal, then:
myImage.layer.cornerRadius = myImage.frame.size.height / 2
Simply you can do that like following
override func viewDidLoad() {
super.viewDidLoad()
profilePhoto.layer.borderWidth = 1
profilePhoto.layer.borderColor = UIColor.black.cgColor
profilePhoto.layer.masksToBounds = false
profilePhoto.layer.cornerRadius = profilePhoto.frame.width/2
profilePhoto.clipsToBounds = true
}
Step1: You can do this by using custom view class. Create a new swift file and set name to UIImageViewX then paste the below code.
import UIKit
#IBDesignable
class UIImageViewX: UIImageView {
// MARK: - Properties
#IBInspectable public var borderColor: UIColor = UIColor.clear {
didSet {
layer.borderColor = borderColor.cgColor
}
}
#IBInspectable public var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
#IBInspectable public var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
}
}
#IBInspectable var pulseDelay: Double = 0.0
#IBInspectable var popIn: Bool = false
#IBInspectable var popInDelay: Double = 0.4
// MARK: - Shadow
#IBInspectable public var shadowOpacity: CGFloat = 0 {
didSet {
//layer.shadowOpacity = Float(shadowOpacity)
}
}
#IBInspectable public var shadowColor: UIColor = UIColor.clear {
didSet {
//layer.shadowColor = shadowColor.cgColor
}
}
#IBInspectable public var shadowRadius: CGFloat = 0 {
didSet {
//layer.shadowRadius = shadowRadius
}
}
#IBInspectable public var shadowOffsetY: CGFloat = 0 {
didSet {
//layer.shadowOffset.height = shadowOffsetY
}
}
// MARK: - FUNCTIONS
override func layoutSubviews() {
// super.layoutSubviews()
// layer.shadowColor = shadowColor.cgColor
// layer.shadowOpacity = Float(shadowOpacity)
// layer.shadowRadius = shadowRadius
// layer.masksToBounds = false
// layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
}
override func draw(_ rect: CGRect) {
if clipsToBounds && shadowOpacity > 0 {
layer.masksToBounds = true
layer.cornerRadius = cornerRadius
// Outer UIView to hold the Shadow
let shadow = UIView(frame: rect)
shadow.layer.cornerRadius = cornerRadius
shadow.layer.masksToBounds = false
shadow.layer.shadowOpacity = Float(shadowOpacity)
shadow.layer.shadowColor = shadowColor.cgColor
shadow.layer.shadowRadius = shadowRadius
shadow.layer.shadowOffset.height = shadowOffsetY
shadow.layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
shadow.addSubview(self)
}
}
override func awakeFromNib() {
if pulseDelay > 0 {
UIView.animate(withDuration: 1, delay: pulseDelay, usingSpringWithDamping: 0.4, initialSpringVelocity: 0, options: [], animations: {
self.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
self.transform = CGAffineTransform.identity
}, completion: nil)
}
if popIn {
transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
UIView.animate(withDuration: 0.8, delay: popInDelay, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: .allowUserInteraction, animations: {
self.transform = CGAffineTransform.identity
}, completion: nil)
}
}
}
Step2:
Set custom class in storyboard like above image.
Step3:
After this you can see the corner radius properties of imageview in Attribute section.
Increase and decrease the corner radius then you will got the desire result
You can round an imageView using the CALayer, use this class to do it in the storyboard:
#IBDesignable class RoundImageView: UIImageView
{
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
}
}
#IBInspectable var borderColor: UIColor? = nil
{
didSet {
layer.borderColor = borderColor?.cgColor
}
}
#IBInspectable var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
#IBInspectable var dropShadowOffset: CGSize = CGSize(width: 0, height: 0) {
didSet {
layer.shadowOffset = dropShadowOffset
}
}
#IBInspectable var maskToBounds: Bool = true {
didSet{
layer.masksToBounds = maskToBounds
}
}
#IBInspectable var dropShadowColor: UIColor? {
didSet {
layer.shadowColor = dropShadowColor?.cgColor
}
}
#IBInspectable var dropShadowRadius: CGFloat = 0 {
didSet {
layer.shadowRadius = dropShadowRadius
}
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
override init(frame: CGRect)
{
super.init(frame: frame)
}
}

Masking UIView/UIImageView to cutout transparent text

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:
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
}
}
This is the result.
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