The problem with gradiant layer in the UIBUTTON in swift - swift

now I am designing button with gradient layer like show in the picture
I have two problems
1- when I add gradient layer to the button the text disappear like show in the last figure
2- The gradient layer I have created does not like the design the left side of the color in the real design alpha less than right ,I have added the right code but does not show what I want
-This parameters what I am using
#IBDesignable
class buttonGeneralDesign: UIButton {
private var gradientLayer: CAGradientLayer!
#IBInspectable
var cornerRadius: CGFloat = 0.0{
didSet{
self.layer.cornerRadius = cornerRadius
self.clipsToBounds = false
}
}
#IBInspectable
var borderColor: UIColor = .clear {
didSet {
self.layer.borderColor = borderColor.cgColor
}
}
#IBInspectable
var borderWidth: CGFloat = 0.0 {
didSet {
self.layer.borderWidth = borderWidth
}
}
#IBInspectable
var shadowColor: UIColor = .clear {
didSet {
self.layer.shadowColor = shadowColor.cgColor
}
}
#IBInspectable
var shadowRadius: CGFloat = 0.0 {
didSet{
self.layer.shadowRadius = shadowRadius
}
}
#IBInspectable
var shadowOpacity: Float = 0.0 {
didSet{
self.layer.shadowOpacity = shadowOpacity
}
}
#IBInspectable
var shadowOffset: CGSize = CGSize.zero{
didSet{
self.layer.shadowOffset = shadowOffset
}
}
#IBInspectable var topColor: UIColor = .red {
didSet {
setNeedsLayout()
}
}
#IBInspectable var bottomColor: UIColor = .yellow {
didSet {
setNeedsLayout()
}
}
#IBInspectable var startPointX: CGFloat = 0 {
didSet {
setNeedsLayout()
}
}
#IBInspectable var startPointY: CGFloat = 0.5 {
didSet {
setNeedsLayout()
}
}
#IBInspectable var endPointX: CGFloat = 1 {
didSet {
setNeedsLayout()
}
}
#IBInspectable var endPointY: CGFloat = 0.5 {
didSet {
setNeedsLayout()
}
}
// override class var layerClass: AnyClass {
// return CAGradientLayer.self
// }
override func layoutSubviews() {
self.gradientLayer = CAGradientLayer()
self.gradientLayer.colors = [topColor.cgColor, bottomColor.cgColor]
self.gradientLayer.startPoint = CGPoint(x: startPointX, y: startPointY)
self.gradientLayer.locations = [0,1]
self.gradientLayer.endPoint = CGPoint(x: endPointX, y: endPointY)
self.gradientLayer.position = self.center
self.layer.insertSublayer( self.gradientLayer, at: 0)
}
}
-This is the real design
This is output of the app

First: don't insert layer in layoutSubview because it calls many
times
Second: Use below or above when inserting layer
Third: use self.gradientLayer.frame = self.bounds instead of self.gradientLayer.position = self.center
.
import UIKit
#IBDesignable
class buttonGeneralDesign: UIButton {
private var gradientLayer: CAGradientLayer!
#IBInspectable
var cornerRadius: CGFloat = 0.0{
didSet{
self.layer.cornerRadius = cornerRadius
self.clipsToBounds = false
}
}
#IBInspectable
var borderColor: UIColor = .clear {
didSet {
self.layer.borderColor = borderColor.cgColor
}
}
#IBInspectable
var borderWidth: CGFloat = 0.0 {
didSet {
self.layer.borderWidth = borderWidth
}
}
#IBInspectable
var shadowColor: UIColor = .clear {
didSet {
self.layer.shadowColor = shadowColor.cgColor
}
}
#IBInspectable
var shadowRadius: CGFloat = 0.0 {
didSet{
self.layer.shadowRadius = shadowRadius
}
}
#IBInspectable
var shadowOpacity: Float = 0.0 {
didSet{
self.layer.shadowOpacity = shadowOpacity
}
}
#IBInspectable
var shadowOffset: CGSize = CGSize.zero{
didSet{
self.layer.shadowOffset = shadowOffset
}
}
#IBInspectable var topColor: UIColor = .red {
didSet {
setNeedsLayout()
}
}
#IBInspectable var bottomColor: UIColor = .yellow {
didSet {
setNeedsLayout()
}
}
#IBInspectable var startPointX: CGFloat = 0 {
didSet {
setNeedsLayout()
}
}
#IBInspectable var startPointY: CGFloat = 0.5 {
didSet {
setNeedsLayout()
}
}
#IBInspectable var endPointX: CGFloat = 1 {
didSet {
setNeedsLayout()
}
}
#IBInspectable var endPointY: CGFloat = 0.5 {
didSet {
setNeedsLayout()
}
}
override init(frame: CGRect) {
super.init(frame: .zero)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit(){
self.gradientLayer = CAGradientLayer()
self.gradientLayer.colors = [topColor.cgColor, bottomColor.cgColor]
self.gradientLayer.startPoint = CGPoint(x: startPointX, y: startPointY)
self.gradientLayer.locations = [0,1]
self.gradientLayer.endPoint = CGPoint(x: endPointX, y: endPointY)
self.layer.insertSublayer( self.gradientLayer, below: self.titleLabel?.layer)
}
override func layoutSubviews() {
self.gradientLayer.frame = self.bounds
}
}

func SetupGradientLeft2Right() {
let gradient:CAGradientLayer = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [UIColor.green.cgColor,UIColor.yellow.cgColor]
gradient.startPoint = CGPoint(x: 0.0, y: 1.0)
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
self.layer.insertSublayer(gradient, at: 0)
}

Related

how to correctly calculate corneRadius of a XIB in swift

cannot set calculated value for the roundedCorners of this xib. only works if Hard coded. So do you have solution? and more generally speaking, is this a good way to make a xib?
this works, but is hard coded
self.contentView.layer.cornerRadius = 12.0
this is too strong effect, clearly not calculating right the value.
self.contentView.layer.cornerRadius = self.frame.width / 2.0
tested some IBInspectable, bu issue Is the same.
// #IBInspectable
// var autoCalculateCornerRadius: Bool = false
// fileprivate var backingCornerRadius: CGFloat = 0
// #IBInspectable
// var cornerRadius: CGFloat {
// set { layer.cornerRadius = newValue }
// get { return layer.cornerRadius }
// }
// #IBInspectable
// var cornerRadius: CGFloat {
// set {
// backingCornerRadius = newValue
// self.layer.cornerRadius = newValue
// }
// get {
// return self.layer.cornerRadius
// }
// }
// override func layoutSubviews() {
// super.layoutSubviews()
// if autoCalculateCornerRadius {
// backingCornerRadius = cornerRadius
// cornerRadius = self.frame.width / 2.0
// } else {
// cornerRadius = backingCornerRadius
// }
// }
import UIKit
class MyCustomViewXib: UIView {
#IBOutlet weak var myImage: UIImageView!
#IBOutlet weak var myLabel: UILabel!
#IBOutlet var contentView: UIView! //the owner view itself
private var viewHeight: CGFloat = 0
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
required override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
deinit {
}
private func setup() {
Bundle.main.loadNibNamed("\(Self.self)", owner: self, options: nil)
addSubview(contentView)
// self.backgroundColor = .clear
viewHeight = contentView.frame.size.height
contentView.frame = self.bounds
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.myLabel.backgroundColor = .red
self.myImage.backgroundColor = .systemTeal
self.contentView.backgroundColor = .systemGreen
self.contentView.layer.cornerRadius = 12.0
// self.contentView.layer.cornerRadius = self.frame.width / 2.0 too much
self.setNeedsLayout()
}
}

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

CornerRadius and Shadow on a UIView using #IBInspectable Swift 4.2

Is it possible to have both CornerRaduis and a shadow on a UIView?
I set up a Custom class for a UIView which uses #IBInspectable to set a cornerRadius and a addShadow which can be true or false. When I set the cornerRadius the shadow doesn't display, if I take away the cornerRadius then it displays again. Thanks in advance!
Custom class:
import UIKit
class CustomUIView: UIView {
override func awakeFromNib() {
self.layer.masksToBounds = cornerRadius > 0
}
#IBInspectable var useDefaultRadius: Bool = true {
didSet {
self.layer.masksToBounds = cornerRadius > 0
}
}
#IBInspectable var cornerRadius: CGFloat {
set {
self.layer.cornerRadius = newValue
}
get {
if (useDefaultRadius) {
// Set default radius
self.layer.cornerRadius = 23
}
return self.layer.cornerRadius
}
}
#IBInspectable var addShadow:Bool = true{
didSet(newValue) {
if(newValue == true){
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 0.5
self.layer.shadowOffset = CGSize(width: 2, height: 3)
self.layer.shadowRadius = 3
self.layer.shadowPath = UIBezierPath(rect: bounds).cgPath
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
print("trying to use shadow")
}
}
}
}
Rather than creating custom clas and changing class of uiview everytime, i prefer extending it.
I'm achieving this by extending UIView as mentioned below:
extension UIView {
#IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
}
}
#IBInspectable
var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
}
}
#IBInspectable
var borderColor: UIColor? {
get {
let color = UIColor.init(cgColor: layer.borderColor!)
return color
}
set {
layer.borderColor = newValue?.cgColor
}
}
#IBInspectable
var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = shadowRadius
}
}
#IBInspectable
var shadowOffset : CGSize{
get{
return layer.shadowOffset
}set{
layer.shadowOffset = newValue
}
}
#IBInspectable
var shadowColor : UIColor{
get{
return UIColor.init(cgColor: layer.shadowColor!)
}
set {
layer.shadowColor = newValue.cgColor
}
}
#IBInspectable
var shadowOpacity : Float {
get{
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
}
And set properties in storyboard as:
That's it.
Hope this helps.
Set true masksToBounds in addShadow:Bool or you don't need to set masksToBounds in addShadow:Bool didSet method
#IBInspectable var addShadow:Bool = true{
didSet(newValue) {
if(newValue == true){
//self.layer.masksToBounds = false
self.layer.masksToBounds = true
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 0.5
self.layer.shadowOffset = CGSize(width: 2, height: 3)
self.layer.shadowRadius = 3
self.layer.shadowPath = UIBezierPath(rect: bounds).cgPath
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
print("trying to use shadow")
}
}
}
You can follow: https://medium.com/bytes-of-bits/swift-tips-adding-rounded-corners-and-shadows-to-a-uiview-691f67b83e4a
https://spin.atomicobject.com/2017/07/18/swift-interface-builder/
Here's the trick, you have to Set true to masksToBounds then Set false to masksToBounds before adding shadow code.
you can check this method to make things clear:
func addShadow(color: UIColor, bottomOrTop: Bool = false, radius: CGFloat = 2, height: CGFloat = 3) {
// These below line makes the trick to draw shadow with corner radius
self.layer.masksToBounds = true
self.layer.masksToBounds = false
self.layer.shadowOffset = CGSize(width: height, height: bottomOrTop ? height : height * -1)
self.layer.shadowRadius = radius
self.layer.shadowColor = color.cgColor
self.layer.shadowOpacity = 0.3
}

Swift 3 xcode storyboard, add box-shadow to UIView, like a CSS style box-shadow

As you can see, I would like to add a shadow around the edges of each UIView in the cells as white on gray hard to see it's borders clearly.
Set your view's shadow properties to add a shadow.
SWIFT 3
YourView.layer.shadowOpacity = 0.7
YourView.layer.shadowOffset = CGSize(width: 3, height: 3)
YourView.layer.shadowRadius = 15.0
YourView.layer.shadowColor = UIColor.darkGray.cgColor
NOTE: replace YourView with the view you want shadow.
here is my approach to this:
You create a new class called ShadowView.
import UIKit
#IBDesignable
class ShadowView: UIView {
//Shadow
#IBInspectable var shadowColor: UIColor = UIColor.black {
didSet {
self.updateView()
}
}
#IBInspectable var shadowOpacity: Float = 0.5 {
didSet {
self.updateView()
}
}
#IBInspectable var shadowOffset: CGSize = CGSize(width: 3, height: 3) {
didSet {
self.updateView()
}
}
#IBInspectable var shadowRadius: CGFloat = 15.0 {
didSet {
self.updateView()
}
}
//Apply params
func updateView() {
self.layer.shadowColor = self.shadowColor.cgColor
self.layer.shadowOpacity = self.shadowOpacity
self.layer.shadowOffset = self.shadowOffset
self.layer.shadowRadius = self.shadowRadius
}
}
Then you can apply this class for you view in the storyboard like:
Now you can edit the values in the attributes inspector:
You can select opacity as you want.
override func awakeFromNib()
{
super.awakeFromNib()
self.viewContainer.layer.shadowOffset = CGSize(width: 0, height: 1) // CGSizeMake(0, 1)
self.viewContainer.layer.shadowColor = UIColor.black.cgColor
self.viewContainer.layer.shadowRadius = 1.5
self.viewContainer.layer.shadowOpacity = 0.65
self.viewContainer.layer.cornerRadius = 1
self.viewContainer.clipsToBounds = true
self.viewContainer.layer.masksToBounds = false
self.layer.masksToBounds = false
}

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