Custom Round UIButton with Round Image View - ios

I have created a custom class for an UIButton which I used to add round corners and drop shadow to my button also using #IBInspectable.
Now, I want to use the same button to create a round one for a profile image picker. The user taps on the round button, the Photo Library is presented, the user selects a photo and the selected image is added to the round button.
My issue is that after the user selects the image, the imageView of the button is square and not round.
I have tried to play with the button's imageView's layer and tried to set it to clipsToBounds or maskToBounds but I think I'm missing something.
Here is the code for the custom button without the ImageView stuff:
import UIKit
class RoundShadowButton: UIButton {
override init(frame: CGRect){
super.init(frame: frame)
imageView?.clipsToBounds = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
imageView?.clipsToBounds = true
}
#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 = newValue
}
}
#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
}
}
}

I think you're missing that unless you're setting image for your button, button's imageView is nil. You should either set maskToBounds to your button, or set it to imageView after you set image. Also, please check your corner radius, if you have method based on your frame, during autolayout you can set your corner radius based on .zero frame

Related

Tab Bar corner radius not showing transparent corners

I'm trying to add a corner radius to my Tab Bar in the top left and top right corners.
Here is my code:
import UIKit
class TabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.tabBar.layer.cornerRadius = 32
self.tabBar.layer.masksToBounds = true
self.tabBar.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
}
}
And here is my Interface Builder for the Tab Bar:
But my Tab Bar comes out looking like this:
As you can see, if you look at the corners, they're not transparent, they're white. It should show the image behind it. So it looks like something is going wrong with masking, but I'm not sure what.
What am I doing wrong?
Here is my other IBDesignable class that might be affecting this:
import UIKit
#IBDesignable
class DesignableView: UIView {
}
#IBDesignable
class DesignableButton: UIButton {
}
#IBDesignable
class DesignableLabel: UILabel {
}
#IBDesignable
class DesignableTextField: UITextField {
}
extension UIView {
#IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
#IBInspectable
var shadowOffset: CGSize {
get {
return layer.shadowOffset
}
set {
layer.shadowOffset = newValue
}
}
#IBInspectable
var shadowColor: UIColor {
get {
return UIColor(cgColor: layer.shadowColor!)
}
set {
layer.shadowColor = newValue.cgColor
}
}
#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 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
}
}
}
Update:
I've found that if I change the background color of the View Controller behind the tabs, the corners of the tab bar match that color:
If I debug the view, you can actually see the little red corners in one of the views:
What does this mean?
I created a project with just the TabBarController. In that, I used your code and set the corner radius, border width and border color.
In the first ViewController, I did the following:
Checked to see if "Under Bottom Bars" is checked in the ViewController.
Added an image and extended it to go under the bottom bar.
It all seemed to work as expected. See image below. May be you can double check your code to see if any of the above steps help.

apply shadow and corner radius to a view, not the entire cell

I have a table view cell, inside which I have a view. In the view, I have 2 labels and an image. I want the view to have shadow and corner radius, not the entire tableview cell. Some suggestions please...
For the shadow, I would create another view behind the actual view, and give it a different x or y position based on where you want the shadow to be angled from the view. Then add your main view as a subclass of that shadow view. Configure the shadow view to look like a shadow by changing the alpha and background color values. For the corner radius, it's changed by view.layer.cornerRadius = x
Make sure to change the corner radius of the shadow view to equal the main views corner radius too, so it looks realistic.
I believe I understood the question correctly, please comment if I did not answer right.
Use this UIView extension in your code. Select your view and in storyboard you can set properties values
#IBDesignable extension UIView {
/* The color of the shadow. Defaults to opaque black. Colors created
* from patterns are currently NOT supported. Animatable. */
#IBInspectable var shadowColor: UIColor? {
set {
layer.shadowColor = newValue!.cgColor
}
get {
if let color = layer.shadowColor {
return UIColor(cgColor:color)
}
else {
return nil
}
}
}
#IBInspectable var clipBounds: Bool {
set {
clipsToBounds = newValue
}
get {
return clipsToBounds
}
}
#IBInspectable var cornerRadius: CGFloat {
set {
layer.cornerRadius = newValue
} get {
return layer.cornerRadius
}
}
#IBInspectable var borderWidth: CGFloat {
set {
layer.borderWidth = newValue
} get {
return layer.borderWidth
}
}
#IBInspectable var borderColor: UIColor? {
set {
layer.borderColor = newValue?.cgColor
}
get {
if let color = layer.borderColor {
return UIColor(cgColor:color)
}
else {
return nil
}
}
}
#IBInspectable var borderColorCode: CGColor? {
set {
layer.borderColor = newValue
} get {
return layer.borderColor
}
}
/* The opacity of the shadow. Defaults to 0. Specifying a value outside the
* [0,1] range will give undefined results. Animatable. */
#IBInspectable var shadowOpacity: Float {
set {
layer.shadowOpacity = newValue
}
get {
return layer.shadowOpacity
}
}
/* The shadow offset. Defaults to (0, -3). Animatable. */
#IBInspectable var shadowOffset: CGPoint {
set {
layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
}
get {
return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
}
}
/* The blur radius used to create the shadow. Defaults to 3. Animatable. */
#IBInspectable var shadowRadius: CGFloat {
set {
layer.shadowRadius = newValue
}
get {
return layer.shadowRadius
}
}
}

IBInspectable not updating in storyboard but works on simulator

I can view the changes made using IBInspectable on my simulator but not in my storyboard
I tried reinstalling my Xcode but it didn't fix this issue. Here are some screenshots :
So i know my code works fine because it runs in the simulator and this is a new project altogether so I do not have any of the "Storyboard might still be loading the changes" problems.
import UIKit
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 {
let color = UIColor(cgColor: layer.borderColor!)
return color
}
set {
layer.borderColor = newValue?.cgColor
}
}
#IBInspectable
var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowOpacity = 0.4
layer.shadowRadius = shadowRadius
}
}
}
Firstly, you need #IBDesignable directive to render the updates in Storyboard whereas #IBInspectable is only to access the property directly in the Storyboard.
Secondly, you have extended the UIView class to show the inspectable properties in the Storyboard but without #IBDesignable.
Now you would think adding #IBDesignable would solve your problem but sadly you can't apply #IBDesignable on an extension like so:
#IBDesignable //won't work on an extension
extension UIView {
//...
}
You can only apply the #IBDesignable directive on a class you have access to, like so:
#IBDesignable
class MyPrettyDesignableView: UIView {
//...
}
Solution:
Bottomline is that you should subclass UIView and apply the #IBDesignable directive on this class.
Basically, your only option is:
#IBDesignable
class MyPrettyDesignableView: UIView {
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
self.layer.cornerRadius = cornerRadius
self.layer.masksToBounds = true
}
}
#IBInspectable var borderWidth: CGFloat = 0 {
didSet {
self.layer.borderWidth = borderWidth
}
}
#IBInspectable var borderColor: UIColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) {
didSet {
self.layer.borderColor = borderColor.cgColor
}
}
}
Then in the storyboard, go to the view that you want designable in storyboard and change it's custom class to MyPrettyDesignableView.
Change it's custom class to your IBDesignable subclass:
Set the IBInspectable properties:
Have you checked that in Interface Builder (Editor Menu) that you have set Automatically Refresh Views and Refresh All Views?
Update: Try this:
#IBDesignable public class CustomView: UIView {
#IBInspectable var borderColor: UIColor = .white{
didSet {
layer.borderColor = borderColor.cgColor
}
}
#IBInspectable var borderWidth: CGFloat = 2.0 {
didSet {
layer.borderWidth = borderWidth
}
}
#IBInspectable var cornerRadius: CGFloat = 10.0 {
didSet {
layer.cornerRadius = cornerRadius
}
}
#IBInspectable var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowOpacity = 0.4
layer.shadowRadius = shadowRadius
}
}
override public func layoutSubviews() {
super.layoutSubviews()
clipsToBounds = true
}
}
Please update your code as below. You must have to set layer.maskToBound = false and view.clipToBounds = false
import UIKit
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 {
let color = UIColor(cgColor: layer.borderColor!)
return color
}
set {
layer.borderColor = newValue?.cgColor
}
}
#IBInspectable
var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowOpacity = 0.4
layer.shadowRadius = shadowRadius
layer.maskToBounds = false
}
}
}
Have you tried to check Automatically Refresh Views or Refresh All Views? It works in my Xcode 9.4.1.
Editor -> Automatically Refresh Views
Assuming you have already applied #IBDesignable correctly at the declaration of a class. And assuming you have already applied #IBInspectable at the properties of the classes, then here is the trick that worked for me.
I delete and retype #IBDesignable.
sometimes even after adding #IBDesignable on the class and #IBInspectable on property definition, your Xcode might not show and refresh, so first
make sure you are using them correctly. if it is not refreshing in my case i had to close all instance of Xcode and restart the Xcode, then it will load the new fresh Interface builder files.

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

Resources