I want to create a Circle image view for my profile avatar. I have tried this:-
class CircleImageView: UIImageView {
override func draw(_ rect: CGRect) {
// Drawing code
layer.masksToBounds = true
layer.cornerRadius = min(rect.width/2 , rect.height/2)
clipsToBounds = true
}
}
But its not working.
An extension will be great to set corner or do round image:
extension UIImageView {
func setRadius(radius: CGFloat? = nil) {
self.layer.cornerRadius = radius ?? self.frame.width / 2;
self.layer.masksToBounds = true;
}
}
Use:
imgview.setRadius(radius: 10)
imgview.setRadius() //default frame.width/2
Draw function is for drawing not changing layer.
Use layoutSubviews
override func layoutSubviews() {
super.layoutSubviews()
layer.masksToBounds = true
layer.cornerRadius = min(self.frame.width/2 , self.frame.height/2)
clipsToBounds = true
}
You are adding the code in the wrong place, drawRect: is not really the right method to do such a functionality for editing the layer, you can achive this by:
Editing the layer when init(frame:) the imageView (also, adding the same functionality in init(coder:) because it should work for both approaches: programmatically and via storyboard):
class CircleImageView: UIImageView {
override init(frame: CGRect) {
super.init(frame: frame)
setupCircleLayer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupCircleLayer()
}
private func setupCircleLayer() {
layer.masksToBounds = true
layer.cornerRadius = min(frame.width/2 , frame.height/2)
clipsToBounds = true
}
}
Or as #Mohammadalijf suggested in his answer by overriding layoutSubviews() method:
class CircleImageView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
layer.masksToBounds = true
layer.cornerRadius = min(frame.width/2 , frame.height/2)
clipsToBounds = true
backgroundColor = UIColor.black
}
}
It does the desired fucntionality that you are asking for, but note that:
Subclasses can override this method as needed to perform more precise
layout of their subviews. You should override this method only if the
autoresizing and constraint-based behaviors of the subviews do not
offer the behavior you want. You can use your implementation to set
the frame rectangles of your subviews directly.
i.e, it is related to updating the layout of the view, check the documentation for more information; That's why I prefer to do it in the init methods.
Related
I have a custom UICollectionViewCell and the problem that I'm facing is rendering the UI right after the cell is being loaded. You can see from the screenshot below, the first cell loads the dimView properly (it has a gradient background color) but any other cell does not.
Worth mentioning is that this view is visible once the user swipe the cell left or right (it creates a flip illusion where it hides an image and shows this view).
If the cell is reused, it's rendered properly, but not when it's initially loaded. Is there another method where I should call this function showDimViewAndCheckmark()??
#IBOutlet weak var backView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
setUI()
}
override func layoutSubviews() {
super.layoutSubviews()
setUI()
}
override func prepareForReuse() {
super.prepareForReuse()
setUI()
}
func setUI() {
coverImageView.clipsToBounds = true
backView.setGradientBackground(colorOne: Colors.purpleDarker, colorTwo: Colors.purpleLight)
backView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOffset = CGSize(width:1, height: 1)
layer.shadowRadius = 2.0
layer.shadowOpacity = 1.0
layer.masksToBounds = false
contentView.clipsToBounds = true
contentView.layer.cornerRadius = 10
contentView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
}
draw(_ rect: CGRect)
Try this method.
Though there is solution of this question is present in internet, but I am unable to do so, I want to round a image.
this code I am using:
extension UIImageView {
func makeRounded() {
let radius = self.frame.width/2.0
self.layer.cornerRadius = radius
self.layer.masksToBounds = true
}
}
then i call this function in viewdidload() like imgvw.makeRounded(). but it is not coming. please help
the previous link is not helping me
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var image: UIImageView!
func makeRounded() {
image.layer.borderWidth = 1
image.layer.masksToBounds = false
image.layer.borderColor = UIColor.blackColor().CGColor
image.layer.cornerRadius = image.frame.height/2 //This will change with corners of image and height/2 will make this circle shape
image.clipsToBounds = true
}
Happy Coding
Overriding viewDidLayoutSubviews will unnessecary call the function makeRounded() because it will get called EVERY TIME some layout happens in the superview. You should use this:
class RoundedImageView: UIImageView {
#override func layoutSubviews() {
super.layoutSubviews()
let radius = self.frame.width/2.0
layer.cornerRadius = radius
clipsToBounds = true // This could get called in the (requiered) initializer
// or, ofcourse, in the interface builder if you are working with storyboards
}
}
Set the class of your imageView to RoundedImageView
My code works well.
avatar.layer.borderWidth = 1
avatar.layer.masksToBounds = false
avatar.layer.borderColor = UIColor(hexString: "#39B44E").cgColor
avatar.layer.cornerRadius = avatar.frame.height/2 //This will change with corners of image and height/2 will make this circle shape
avatar.clipsToBounds = true
Create an extension for your class
extension ViewController: UIViewController{
func makeRounded() {
layer.borderWidth = 1
layer.masksToBounds = false
layer.borderColor = UIColor.blackColor().CGColor
layer.cornerRadius = frame.height/2
clipsToBounds = true
}
}
Then call use it
imageView.makeRounded()
I've been setting the backgroundColor of buttons by doing self.layer.backgroundColor = someColor.
However, this doesn't seem to work with a custom class? I have this general class that I use:
class DarkButton: BaseButton {
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = 5
self.setTitleColor(UIColor.black, for: .normal)
self.setTitleColor(UIColor.lightGray, for: .highlighted)
self.layer.backgroundColor = UIColor(red:0.77, green:0.77,
blue:0.77, alpha: 1.0).cgColor
self.clipsToBounds = false
}
}
the self.layer.backgroundColor works just fine. Now if I extend it, like so:
class SuperCoolButton: DarkButton {
required init() {
super.init()
self.setUp()
}
required init(spacing: Spacing) {
super.init(spacing: spacing)
self.setUp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setUp()
}
func setUp() {
self.generateImage()
self.changeBGColor()
}
func generateImage() {
let image = UIImage(named: "logoSmall") as UIImage?
self.setImage(image, for: .normal)
self.imageEdgeInsets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)
self.imageView?.contentMode = .scaleAspectFit
}
func changeBGColor() {
self.layer.backgroundColor = UIColor.red.cgColor
}
}
the backgroundColor stays that of the DarkButton backgroundColor, however setting the image does indeed work >_>
This is happening because layoutSubviews() implemented in the super class(DarkButton) gets called after setUp method of DarkButton. You need to override layoutSubviews in your class SuperCoolButton and call setUp there instead of at its init method.
override func layoutSubviews() {
super.layoutSubviews()
setUp()
}
EDIT:
I think you should move the code which you have written inside [layoutSubviews][1] of DarkButton class.
layoutSubviews method gets called multiple times and only the code related to the layout of the view should be there.
Subclasses can override this method as needed to perform more precise
layout of their subviews. You should override this method only if the
autoresizing and constraint-based behaviors of the subviews do not
offer the behavior you want.
The ideal place to change layer's corner radius and setTitleColor or background color is either at init of the custom view or at awakeFromNib:(only, if the view is always going to be designed in nib).
Because you want to change the backgroud color of button at some later time. You can simply call your changeBgColor method on SuperCoolButton. Earlier it was not working, because layoutSubviews must be setting the background color back to the default.
Do not call your setUp() method in init instead of this call it in layoutSubviews
class SuperCoolButton: DarkButton {
required init() {
super.init()
//self.setUp()
}
required init(spacing: Spacing) {
super.init(spacing: spacing)
//self.setUp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//self.setUp()
}
func setUp() {
self.generateImage()
self.changeBGColor()
}
func generateImage() {
let image = UIImage(named: "logoSmall") as UIImage?
self.setImage(image, for: .normal)
self.imageEdgeInsets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)
self.imageView?.contentMode = .scaleAspectFit
}
func changeBGColor() {
self.layer.backgroundColor = UIColor.red.cgColor
}
override func layoutSubviews() {
super.layoutSubviews()
setUp()
}
}
I have a custom UIView and I add it to my ViewController like this:
let myCustomView = Bundle.main.loadNibNamed("MyCustomView", owner: nil, options: nil) as! MyCustomView
myCustomView.layer.cornerRadius = 10
myCustomView.layer.masksToBounds = true
I round the corners of the view. But I am wondering, is there a way to move this logic of rounding the corners inside the MyCustomView class?
As you use IB, you may find it more convenient to make an extension of UIView
extension UIView {
#IBInspectable var borderColor: UIColor? {
set {
layer.borderColor = newValue?.cgColor
}
get {
if let color = layer.borderColor {
return UIColor(cgColor:color)
} else {
return nil
}
}
}
#IBInspectable var borderWidth: CGFloat {
set {
layer.borderWidth = newValue
}
get {
return layer.borderWidth
}
}
#IBInspectable var cornerRadius: CGFloat {
set {
layer.cornerRadius = newValue
clipsToBounds = newValue > 0
}
get {
return layer.cornerRadius
}
}
}
Then you can set those values from Attributes inspector.
Yes - If you're loading a nib with a custom view, that nib is very likely referring to another class. If that's the case, you can move the logic inside the class itself.
That said, I really like Lawliet's suggestion of making a UIView extension with IBInspectable properties. The downside to that approach is that every single view now has these properties, which creates a certain overhead and potential for clashes.
You can do something like this in your UIView subclass:
class RoundedView: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.cornerRadius = 10
self.layer.masksToBounds = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.cornerRadius = 10
self.layer.masksToBounds = true
}
}
Also if you want to pass a custom value instead of '10' in the cornerRadius property, you can try to implement a convenience init by looking here:
Override Init method of UIView in Swift
I have a collectionView cell that should either display an image or an icon that is generated as a custom UIView (lets say IconView).
Currently, I implemented this by adding an UIImageView and an IconView as subviews to a container view.
When an image is provided, the image property of UIImageView is simply updated. When a new IconView is provided it is currently always added as a subview to the container view. Therefore, before adding, it is first checked whether an IconView has already been added, and if so it is removed.
Although this implementation works, it is not very elegant and seems not efficient since it results in scrolling issues when the number of rows increase.
Would there be a better (more efficient) way to implement this for a single CollectionViewCell?
class CustomCell: UICollectionViewCell {
internal var image: UIImage? {
didSet {
self.imageView.image = image!
}
}
internal var iconView: IconView? {
didSet {
if !(self.iconContainerView.subviews.flatMap{ $0 as? IconView}.isEmpty) {
self.iconView!.removeFromSuperview()
}
self.iconView!.translatesAutoresizingMaskIntoConstraints = false
self.iconContainerView.addSubview(self.iconView!)
self.image = nil
}
}
fileprivate var imageView: UIImageView!
fileprivate var iconContainerView: UIView!
fileprivate var layoutConstraints = [NSLayoutConstraint]()
override init(frame: CGRect) {
super.init(frame: frame)
// ContainerView
self.iconContainerView = UIView()
self.iconContainerView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.iconContainerView)
// ImageView
self.imageView = UIImageView()
self.imageView.translatesAutoresizingMaskIntoConstraints = false
self.iconContainerView.addSubview(self.imageView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
self.iconContainerView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor).isActive = true
self.iconContainerView.widthAnchor.constraint(equalToConstant: 60).isActive = true
self.iconContainerView.heightAnchor.constraint(equalToConstant: 60).isActive = true
self.iconContainerView.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor).isActive = true
// Deactivate non-reusable constraints
_ = self.layoutConstraints.map { $0.isActive = false }
self.layoutConstraints = [NSLayoutConstraint]()
if let iconView = self.iconView {
self.imageView.isHidden = true
self.layoutConstraints.append(iconView.centerYAnchor.constraint(equalTo: self.iconContainerView.centerYAnchor))
self.layoutConstraints.append(iconView.centerXAnchor.constraint(equalTo: self.iconContainerView.centerXAnchor))
self.layoutConstraints.append(iconView.heightAnchor.constraint(equalToConstant: 40))
self.layoutConstraints.append(iconView.widthAnchor.constraint(equalToConstant: 40))
} else {
self.imageView.isHidden = false
self.iconView?.isHidden = true
self.layoutConstraints.append(self.imageView.leadingAnchor.constraint(equalTo: self.iconContainerView.leadingAnchor))
self.layoutConstraints.append(self.imageView.trailingAnchor.constraint(equalTo: self.iconContainerView.trailingAnchor))
self.layoutConstraints.append(self.imageView.topAnchor.constraint(equalTo: self.iconContainerView.topAnchor))
self.layoutConstraints.append(self.imageView.bottomAnchor.constraint(equalTo: self.iconContainerView.bottomAnchor))
}
_ = self.layoutConstraints.map {$0.isActive = true}
}
}
Don't ad and remove the IconView when setting. Add both in the same spot and change the isHidden, alpha, or opacity or bringSubviewToFront. This is much less main thread intensive.