UIButton, Autolayout and getting the height of the button - ios

I'm trying to get the real height of my custom UIButton. But I always get a value that is way smaller than I expected. I get something around 30 but expected something about 45.
override func awakeFromNib() {
print(self.frame.height)
self.layer.cornerRadius = self.layout.frame.height / 2.0
self.clipsToBounds = true
self.titleLabel?.adjustsFontSizeToFitWidth = true
}
This is my code, but at runtime somehow autolayout changes the size, which is perfect, but I can not set the right cornerRadius (always too small). So maybe I need the multiplier or something like that. I already testet with adding the contentEdgeInsets of top and bottom, but it made no difference.
Thanks
Tobias

The view hasn't been laid out in awakeFromNib(). Set your corner radius when layout has guaranteed to have happened.
For a view:
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = frame.height / 2.0
}
For a UIViewController:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.layer.cornerRadius = view.frame.height / 2.0
}

Try calling self.layoutIfNeeded() before you print the size

Related

Swift UITableViewCell Shadow not appearing

I am trying to create custom table view cell which works fine in my other UIViewControllers. However, in one of my controllers, the shadow is not growing, I can barely see the shadow.
Here is an image of the shadow being shown in red, you can see it is barely visible.
My cell has a UIView added inside the contentView to creating floating cell effects - the same code and same storyboard layouts are being used across my controllers but this is the only table view where the shadow issue is occurring - so I must be missing something.
My addShadow extension:
extension UIView {
func addShadow(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float) {
layer.masksToBounds = false
layer.shadowOffset = offset
layer.shadowColor = color.cgColor
layer.shadowRadius = radius
layer.shadowOpacity = opacity
}
}
My awakeFromNib on the custom cell:
:: cellContentView is my UIView added to the base contentView of the cell.
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = .clear
self.selectionStyle = .none
cellContentView?.layer.masksToBounds = true
cellContentView?.round(corners: [.topLeft, .topRight, .bottomLeft, .bottomRight], radius: 10)
cellContentView?.addShadow(offset: CGSize(width: 40, height: 60), color: UIColor.red, radius: 10, opacity: 1)
cellContentView?.layer.shouldRasterize = true
}
Note: The .round is an extension being used on all my cells.
No matter what radius or offset I add for this shadow, it does not get bigger than the image. Also, none of my other cells in the their controllers require the shouldRasterize property to be set, but this does.
Does anyone know what is happening here?
Thanks :)
Edit
Strangely, if I add constraints around my view to keep the gaps large between my view and the cell content view, the background colour disappears - this is set to white in the storyboard.
You should call in the layoutSubviews method. because shadow should add after the view is uploaded
override func awakeFromNib() {
super.awakeFromNib()
//init methods
}
override public func layoutSubviews() {
super.layoutSubviews()
//Added shadow
self.reloadLayers()
}
private func reloadLayers() {
self.layer.cornerRadius = 5
self.addShadow(.TransactionCell)
}
I hope it helps
Content view will fill you cell, so you need to add shadow to view inside content view which has all your components inside it. Then add constraints to it with gap between that view and content view. Second, 40 and 60 properties for shadow is likely too large, when I said too large I mean unbelievable large, because gap between content views in cells are no more than 15 - 30 even less. so try it with much less values, while radius can remain 10 but you will see what value fit the best. If cell content view is your custom view just values will did the job if your view is not kind of transparent or any inside it, in that case it won't, and there is hard to fix that, I tried many libraries and custom codes and it is never ok.
squircleView.layer.cornerRadius = 40
squircleView.layer.cornerCurve = CALayerCornerCurve.continuous
squircleView.layer.shadowColor = UIColor.systemGray.cgColor
squircleView.layer.shadowOpacity = 0.7
squircleView.layer.shadowOffset = CGSize(width: 0, height: 0.5)
squircleView.layer.shadowRadius = 5

Storyboard Set Width / Height for Custom View in Autolayout Similar to UILabel

Labels are really convenient in iOS autolayout because they don't require a constraints to determine size, they only require an X and a Y position. For static text this is great.
I have a custom view that I would like to provide a default size to auto layout similar to UILabels. Is this possible? I am familiar with IBInspectable and IBDesignable, but I'm not sure how to implement this. I am using autolayout in storyboards, but I imagine the solution would work for storyboards + programmatic.
I know I can just set the height and width, but this is view that will be used everywhere so it would be nice to have the width / height dynamic.
Unfortunately, there is no smooth way to add UIView without specifying its size values, i.e. its width and height.
Speaking of auto-layout, there are 2 options you got, probably you are already aware of too. 1) you should either set UIView's size values, or 2. you set other UI Objects' sizes so that auto-layout can understand the size of UIView.
Speaking of programmatically, upon creating UIView object, if you do not provide frame, then it is not shown. Although this might be a solution to put at any point with any size, it might be not ideal when you are using xibs or storyboards, since there would be a gap on those interfaces which may also confuse development.
The way I am thinking of includes using both intrinsicContentSize and IBDesignable. I made a little demo for this purpose and you can find this code below. I will share 2 examples, one is inherited from UILabel and the other is inherited from UIView, so that I can show usage of intrinsicContentSize easier.
Below is the UIView one.
import UIKit
#IBDesignable class UIDemoView: UIView {
func setup() {
layer.cornerRadius = frame.height / 5
clipsToBounds = true
}
override func awakeFromNib() {
super.awakeFromNib()
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
self.setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.setup()
}
override var intrinsicContentSize: CGSize {
let newWidth = 100
let newHeight = 100
let newSize = CGSize(width: newWidth, height: newHeight)
return newSize
}
}
And below is the UILabel one.
import UIKit
#IBDesignable class UIDemoLabel: UILabel {
func setup() {
layer.cornerRadius = frame.height / 2
clipsToBounds = true
textAlignment = .center
}
override func awakeFromNib() {
super.awakeFromNib()
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
self.setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.setup()
}
override var intrinsicContentSize: CGSize {
let superSize = super.intrinsicContentSize
let newWidth = superSize.width + superSize.height
let newHeight = superSize.height
let newSize = CGSize(width: newWidth, height: newHeight)
return newSize
}
}
Here is the output on auto-layout
I intentionally showed the constraints on both object, so you can check if I understand you correctly. They both have only 2 constraints: top and center alignment.
UILabels have theirs own intrinsicContentSize, so when overriding them we might use their already given intrinsicContentSize as a reference, as a guidance to whatever we want to do. UIViews intrinsicContentSize is not set, therefore we must find a way to give them specific sizes. With not being sure, I am guessing for UILabel intrinsicContentSize implementation, logic is most probably related with the size of the text. You can simply pass constants like above code, or you can use any custom logic to provide CGFloats.
Just a thought of mine: Even though I came with this solution, I am not fan of IBDesignables. From my perspective, they slow down the development because of its buggy nature. Thus, to be fully honest, I'd rather 1. putting UIView objects on xibs and storyboards, 2. changing its class from UIView to custom UIView class, 3. settings its constraints, 4. setting remaining properties programmatically from its IBOutlet. This is more of a traditional way, tho :)
Hope this helps you!
You can override
var intrinsicContentSize: CGSize { get }
property of UIView and calculate and return the height and width of the view based on its contents.
Ref : intrinsicContentSize

UIlabel Sizetofit unexpected behavious

I am facing an unexpected behavior with my UIlabel sizeToFit() method. Now I have tried making the numberOfLines = 0, I event called LayoutIfNeed(). But None of them works.
I even tried methods given in this question : Vertically align text to top within a UILabel
But Again None helped. I don't have to many constraints, I am just using Auto resizing pins. I even tried it with no constraints or no auto resize.
I have this label set up in TableViewCell and CollectionReusableView. Calling it in awakeFromNib() just doesnt affect.
UIcollectionReusableView code :
override func awakeFromNib() {
super.awakeFromNib()
label.sizeToFit()
label.numberOfLines = 0
label.layer.shadowOffset = CGSize(width: 0, height: 0)
label.layer.shadowOpacity = 3
label.layer.shadowRadius = 8
}
constrains :
TableViewCell Code:
override func layoutSubviews() {
super.layoutSubviews()
// this is the UIview on which the label is put on.
contentView.layoutIfNeeded()
}
override func awakeFromNib() {
super.awakeFromNib()
// This is the label has the issue
caption.sizeToFit()
}
Constraints :
Also, Label in the text view is stacked with Another Label and stackview constrainst are :
here is the example:
It Just doesn't seem to work. I am totally out of idea.
Any help is greatly appreciated.
You need to call sizeToFit() after the text is added to the label, not before.

Circular views with Autolayout (snapkit)?

I am trying to make a circular view which has an adaptive size based on auto layout, currently i set the constraints, then i attempt to round the image in the viewwilllayoutsubviews method.
This is resulting in oddly shaped views that are not circular, how can i resolve this?
init:
profilePic = UIImageView(frame: CGRect.zero)
profilePic.clipsToBounds = true
profilePic.contentMode = .scaleAspectFill
constrains:
profilePic.snp.makeConstraints { (make) -> Void in
make.centerX.equalTo(self).multipliedBy(0.80)
make.centerY.equalTo(self).multipliedBy(0.40)
make.size.equalTo(self).multipliedBy(0.22)
}
subviews:
override func viewWillLayoutSubviews() {
self.navigationMenuView.profilePic.layer.cornerRadius = self.navigationMenuView.profilePic.frame.size.width / 2.0
self.navigationMenuView.profilePic.layer.borderWidth = 2
self.navigationMenuView.profilePic.layer.borderColor = UIColor.white.cgColor
}
result:
I guess you want this (sorry for the plain autolayout, but I don't use snapkit):
profilePic.heightAnchor.constraint(equalTo: profilePic.widthAnchor).isActive = true
profilePic.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.22).isActive = true
Instead of this:
make.size.equalTo(self).multipliedBy(0.22)
I had the same problem
This is my solution:
let profilePicHeight: CGFloat = 30.0
Add this line of code to your constrains:
profilePic.snp.makeConstraints { (make) -> Void in
make.height.width.equalTo(self.profilePicHeight)
...
}
then:
override func viewWillLayoutSubviews() {
self.navigationMenuView.profilePic.layer.cornerRadius = self.profilePicHeight / 2.0
...
}
My suggestion here is don't treat it like a circular view from the outside of it. Make the view itself conform to being a circle so that you can use it anywhere.
INSIDE the view give it constraints like...
widthAnchor.constraint(equalTo: heightAnchor).isActive = true
This will make it square (with undetermined size).
Then in the function layoutSubviews...
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.size.width * 0.5
}
This will make the square into a circle.

Perfect round corners on rectangular view

I have a rectangular view that I want "perfectly" round borders on the ends. I'm rounding the corners like so:
contentView.layer.cornerRadius = 30; //arbitrary number
contentView.layer.borderWidth = 1.0
contentView.layer.borderColor = UIColor.white.cgColor
And this is my result:
And this is the result I'm aiming for:
I'd like to determine the cornerRadius for my view to achieve rounded ends dynamically. Any ideas? Thanks!
You’re on the right way. Somewhat conventional approach to do it looks like this:
contentView.layer.cornerRadius = contentView.bounds.height / 2
If your "contentView" is a class that inherits from a UIView then:
class CustomView: UIView
{
...
override func layoutSubviews()
{
super.layoutSubviews() //Don't want to change the default behavior of this func. Just want to add to it.
layer.cornerRadius = bounds.height / 2
}
...
}
If it's just a plain UIView in a UIViewController, then you can update it in the UIViewController's viewWillAppear(_ animated: Bool) method.

Resources