How setting up shadow of the CALayer breaks AutoLayout constraints? - ios

I have cell's imageView shadow customization:
override func layoutSubviews() {
moviePosterImageView.layer.shadowOffset = .zero
moviePosterImageView.layer.shadowColor = UIColor.black.cgColor
moviePosterImageView.layer.shadowRadius = 5
moviePosterImageView.layer.shadowOpacity = 1
moviePosterImageView.layer.masksToBounds = false
moviePosterImageView.layer.shadowPath = UIBezierPath(rect: moviePosterImageView.bounds).cgPath
}
But it breaks imageView sizes and constraints. Without shadow it works pretty good.

But it breaks imageView sizes and constraints.
Yes, because you forgot to start by saying
override func layoutSubviews() {
super.layoutSubviews() // important
Since autolayout happens in layoutSubviews, if you don’t call super you prevent it from operating.

Use This Code in another swift File and set shadow in story board or xib easily
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 {
if let color = layer.borderColor {
return UIColor(cgColor: color)
}
return nil
}
set {
if let color = newValue {
layer.borderColor = color.cgColor
} else {
layer.borderColor = nil
}
}
}
#IBInspectable
var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = newValue
}
}
#IBInspectable
var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
#IBInspectable
var shadowOffset: CGSize {
get {
return layer.shadowOffset
}
set {
layer.shadowOffset = newValue
}
}
#IBInspectable
var shadowColor: UIColor? {
get {
if let color = layer.shadowColor {
return UIColor(cgColor: color)
}
return nil
}
set {
if let color = newValue {
layer.shadowColor = color.cgColor
} else {
layer.shadowColor = nil
}
}
}
}

Related

how to set gradient layer width to be the same as button width?

I am new in iOS Development, I want to make gradient rounded button by using this designable class
#IBDesignable
class GradientButton: UIButton {
let gradientLayer = CAGradientLayer()
#IBInspectable
var topGradientColor: UIColor? {
didSet {
setGradient(topGradientColor: topGradientColor, bottomGradientColor: bottomGradientColor)
}
}
#IBInspectable
var bottomGradientColor: UIColor? {
didSet {
setGradient(topGradientColor: topGradientColor, bottomGradientColor: bottomGradientColor)
}
}
override func layoutSubviews() {
super.layoutSubviews()
updateCornerRadius()
}
#IBInspectable var fullRounded: Bool = false {
didSet {
updateCornerRadius()
}
}
#IBInspectable var cornerRadiusOfButton : CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadiusOfButton
}
}
func updateCornerRadius() {
layer.cornerRadius = fullRounded ? (frame.size.height / 2) : cornerRadiusOfButton
}
#IBInspectable var borderWidth: CGFloat {
set {
layer.borderWidth = newValue
}
get {
return layer.borderWidth
}
}
#IBInspectable var borderColor: UIColor? {
set {
guard let uiColor = newValue else { return }
layer.borderColor = uiColor.cgColor
}
get {
guard let color = layer.borderColor else { return nil }
return UIColor(cgColor: color)
}
}
private func setGradient(topGradientColor: UIColor?, bottomGradientColor: UIColor?) {
if let topGradientColor = topGradientColor, let bottomGradientColor = bottomGradientColor {
gradientLayer.frame = bounds
gradientLayer.colors = [topGradientColor.cgColor, bottomGradientColor.cgColor]
gradientLayer.borderColor = layer.borderColor
gradientLayer.borderWidth = layer.borderWidth
gradientLayer.cornerRadius = layer.cornerRadius
layer.insertSublayer(gradientLayer, at: 0)
} else {
gradientLayer.removeFromSuperlayer()
}
}
}
but here is the result:
as you can see the width of the gradient layer button is incorret.
here is the constraint layout I use:
I want that gradient layer to have the same width as the original button, I suspect that the problem is in this line
gradientLayer.frame = bounds
but unfortunately I don't know how to set the gradient layer width to be the same size as the original button programmatically in #IBDesignable class
could you please help me to solve this issue ? thanks in advance
Override layoutSubviews inside GradientButton and update the gradientLayer frame as,
override func layoutSubviews() {
super.layoutSubviews()
self.gradientLayer.frame = bounds
}

How can I fix this warning msg. Warning: Ignoring user defined runtime attribute for key path "radius" on instance of "UIButton"

I am getting this warning message. How can I fix this.
Warning: IB Designables: Ignoring user defined runtime attribute for key path radius on instance of UIButton. Hit an exception when attempting to set its value: [ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key radius.
Here is custom UIButtonRounded class
#IBDesignable class UIButtonRounded: UIButton
{
override func layoutSubviews() {
super.layoutSubviews()
updateCornerRadius()
}
#IBInspectable var rounded: Bool = false {
didSet {
updateCornerRadius()
}
}
#IBInspectable var border: Bool = false {
didSet {
updateCornerRadius()
}
}
#IBInspectable var radious: CGFloat = 0 {
didSet {
updateCornerRadius()
}
}
func updateCornerRadius() {
layer.cornerRadius = rounded ? radious : 0
layer.masksToBounds = true
if(border){
layer.borderWidth = 1.3
layer.borderColor = UIColor(named:"color_bg_white")?.cgColor
}
}
}
Thanks In advance.
If you make a value and change it later it will still exists in the user defined runtime attributes in the Identity Inspector. Go there and delete the old value.
It's working for me:
import UIKit
#IBDesignable
class DesignableView: UIView {
}
#IBDesignable
class DesignableButton: UIButton {
}
#IBDesignable
class DesignableLabel: UILabel {
}
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 {
if let color = layer.borderColor {
return UIColor(cgColor: color)
}
return nil
}
set {
if let color = newValue {
layer.borderColor = color.cgColor
} else {
layer.borderColor = nil
}
}
}
#IBInspectable
var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = newValue
}
}
#IBInspectable
var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
#IBInspectable
var shadowOffset: CGSize {
get {
return layer.shadowOffset
}
set {
layer.shadowOffset = newValue
}
}
#IBInspectable
var shadowColor: UIColor? {
get {
if let color = layer.shadowColor {
return UIColor(cgColor: color)
}
return nil
}
set {
if let color = newValue {
layer.shadowColor = color.cgColor
} else {
layer.shadowColor = nil
}
}
}
}

Why properties tagged #IBInspectable is not working?

I followed the tutorial here to create an inspectable and designable view. I just want to add border color, border width, and rounded border capability into the UIView.
I have been successful to show the properties. But doesn't matter what I set on the storyboard, the result is still like if they aren't there. There's no border, even though I've set the border to be 2 in width and black in color. It's not showing both on the storyboard and at the run time. I have set the border to be 2 width, but at run time, I print the border width value at didViewLoad, and the result is 0. What could be possibly wrong?
#IBDesignable extension view: 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 = borderWidth;
}
}
#IBInspectable var borderColor: UIColor? {
get {
return UIColor(CGColor: layer.borderColor!);
}
set {
layer.borderColor = borderColor?.CGColor
}
}
}
And this doesn't work either:
#IBDesignable class BOView: 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 = borderWidth;
}
}
#IBInspectable var borderColor: UIColor? {
get {
return UIColor(CGColor: layer.borderColor!);
}
set {
layer.borderColor = borderColor?.CGColor
}
}
}
Try using newValue instead of the real value.
For instance,
#IBInspectable var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
}
}
Chen, I do prefer the way you are doing it, but also note that what you are trying to do is possible from Storyboard directly without using IBDesignable. You can set the layer properties in User Defined Runtime Attributes.

iOS button design

What is the best way to design custom buttons and using them within Xcode. I want them not to look like random buttons. They should look like button as the were from Apple themselves.
You can use just the UIButton, you can set easily:
background color, background image
font type, font color
round corner
dimensions even dynamics with the right constraints
What kind of button do you want to make? See this link for a basic tutorial meantime.
Create a new file called design.swift then add these:
import Foundation
import UIKit
#IBDesignable class CustomTextView : UITextView
{
#IBInspectable var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
#IBInspectable var borderWidth: CGFloat{
set {
layer.borderWidth = newValue
}
get{
return self.layer.borderWidth
}
}
#IBInspectable var borderColor: UIColor? {
set {
layer.borderColor = newValue?.CGColor
}
get{
let c = UIColor(CGColor: layer.borderColor!)
return c
}
}
}
#IBDesignable class CustomView : UIView{
#IBInspectable var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
#IBInspectable var borderWidth: CGFloat{
set {
layer.borderWidth = newValue
}
get{
return self.layer.borderWidth
}
}
#IBInspectable var borderColor: UIColor? {
set {
layer.borderColor = newValue?.CGColor
}
get{
let c = UIColor(CGColor: layer.borderColor!)
return c
}
}
}
#IBDesignable class CustomButton : UIButton{
#IBInspectable var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
#IBInspectable var borderWidth: CGFloat{
set {
layer.borderWidth = newValue
}
get{
return self.layer.borderWidth
}
}
#IBInspectable var borderColor: UIColor? {
set {
layer.borderColor = newValue?.CGColor
}
get{
let c = UIColor(CGColor: layer.borderColor!)
return c
}
}
}
then click at your button go to class right Custom

UIButton Border Color on Selected State using IBInspectable

I am using #IBInspectable to set a border color on a UIButton using a Swift extension like this:
extension UIButton {
#IBInspectable var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
layer.masksToBounds = newValue > 0
}
}
}
...but I also want to have a selectedBorderColor property for the .Selected state of the button. Is this possible with an extension like this? Do I have to subclass UIButton and check the button state somehow instead?
extension UIButton {
#IBInspectable var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
layer.masksToBounds = newValue > 0
}
}
//This is what I want to do...
#IBInspectable var selectedBorderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
layer.masksToBounds = newValue > 0
}
}
}
Thanks in advance.
You can subclass UIButton like this:
class SBButton: UIButton {
#IBInspectable var borderColor : UIColor = UIColor.whiteColor() {
didSet {
self.setNeedsDisplay()
}
}
#IBInspectable var selectedBorderColor : UIColor = UIColor.blackColor() {
didSet {
self.setNeedsDisplay()
}
}
override var selected: Bool {
didSet {
if selected {
layer.borderColor = selectedBorderColor.CGColor
} else {
layer.borderColor = borderColor.CGColor
}
}
}
}
and use button.selected = true

Resources