I have a class that manages positioning of UIKit elements using 'frame':
class EasyPos: UIView {
var screenWidth = UIScreen.main.bounds.width
var screenHeight = UIScreen.main.bounds.height
var x: CGFloat = 0 { didSet { self.updateFrame() } }
var y: CGFloat = 0 { didSet { self.updateFrame() } }
var width: CGFloat = 0 { didSet { self.updateFrame() } }
var height: CGFloat = 0 { didSet { self.updateFrame() } }
func updateFrame() {
frame = CGRect(x: x, y: y, width: width, height: height)
if x < 0 {
frame = CGRect(x: screenWidth - abs(x) - width, y: frame.minY, width: frame.width, height: frame.height)
}
if y < 0 {
frame = CGRect(x: frame.minX, y: screenHeight - abs(y) - height, width: frame.width, height: frame.height)
}
}
required init(x: CGFloat = 0, y: CGFloat = 0, width: CGFloat = 40, height: CGFloat = 40) {
self.x = x
self.y = y
self.width = width
self.height = height
super.init(frame: .zero)
updateFrame()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now I want to add one class that inherits from UIbutton and one that inherits from a UILabel. And they should both be able to use the methods from the EasyPos class. How do I do this? (I tried inheritance from two classes, didn't work)
I know it has something to do with protocols. But I couldn't get this to work either.
Thanks for you help ;)
One way you could do this is to write a protocol extension. Because you have properties you'll need to implement a component but it shouldn't be too difficult -
struct FrameComponent {
var x: CGFloat = 0
var y: CGFloat = 0
var width: CGFloat = 0
var height: CGFloat = 0
}
protocol EasyPos {
var fc: FrameComponent { get, set }
func updateFrame()
}
extension EasyPos where Self: UIView {
func updateFrame() {
frame = CGRect(x: fc.x, y: fc.y, width: fc.width, height: fc.height)
if fc.x < 0 {
frame = CGRect(x: screenWidth - abs(fc.x) - fc.width, y: frame.minY, width:
frame.width, height: frame.height)
}
if fc.y < 0 {
frame = CGRect(x: frame.minX, y: screenHeight - abs(fc.y) - fc.height, width:
frame.width, height: frame.height)
}
}
}
Something you should remember is extensions can't add property observers, so you will need to call updateFrame() manually.
Related
I've got multiple UIImageViews, spread across in a view.
#IBOutlet var leftIVs: [UIImageView]!
#IBOutlet var topIVs: [UIImageView]!
#IBOutlet var rightIVs: [UIImageView]!
I'm trying to create a function using UIView.animate... functions and 'transform' property which brings all of these UIImageViews at the center of the superview. I'm using the following code:
let performInitialTransformation: ((UIImageView)->()) = { (card) in
let cardCenter = CGPoint(x: card.frame.midX, y: card.frame.midY)
let viewCenter = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY)
let deltaPoint = cardCenter - viewCenter //also tried (viewCenter - cardCenter)
UIView.animate(withDuration: 0.5, animations: {
card.transform = CGAffineTransform.init(translationX: deltaPoint.x, y: deltaPoint.y)
}) { (done) in
}
}
for card in topIVs {
performInitialTransformation(card)
}
for card in leftIVs {
performInitialTransformation(card)
}
for card in rightIVs {
performInitialTransformation(card)
}
I'm using this static function:
extension CGPoint {
static func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
return CGPoint(x: rhs.x - lhs.x, y: rhs.y - lhs.y)
}
}
NOTE: Also, I will be bringing those images back to there original position afterward for which I will use CGAffineTransform.identity
The images are not being shown in the center. How can I achieve this? Thanks in advance!
I think your problem was that you didn't reduce the size of the image view so that they stack at the exact centre of the screen.
let deltaX = (self.view.center.x - card.frame.minX) - card.frame.width/2
let deltaY = (self.view.center.y - card.frame.minY) - card.frame.height/2
UIView.animate(withDuration: 1) {
card.transform = .init(translationX: deltaX, y: deltaY)
}
What is about to use center
UIView.animate(withDuration: 0.5) {
images.forEach { $0.center = superview.center }
}
The full example
var subviewes = [UIView]()
var view = UIView(frame: CGRect.init(x: 0, y: 0, width: 30, height: 30))
view.backgroundColor = .yellow
subviewes.append(view)
view = UIView(frame: CGRect.init(x: 0, y: 0, width: 20, height: 20))
view.backgroundColor = .green
subviewes.append(view)
view = UIView(frame: CGRect.init(x: 0, y: 0, width: 10, height: 10))
view.backgroundColor = .red
subviewes.append(view)
subviewes.forEach { self.view.addSubview($0) }
UIView.animate(withDuration: 0.5) {
subviewes.forEach { $0.center = self.view.center }
}
I'm trying to zoom image and crop image along the focusView line.
But image's position isn't center while scrolling.
Also cropImage() doesn't crop properly.
I've already tried below code.
setup() image shows center but after scrolling, image position isn't center.
Also cropImage()
class CropImageView: UIView {
#IBOutlet weak var focusView: UIImage! // crop frame
#IBOutlet weak var scrollView: UIScrollView!
var previewImage: UIImage?
var previewImageView: UIImageView!
var zoomScaleView: UIView!
func setup() {
scrollView.delegate = self
zoomScaleView = UIView(frame: .zero)
scrollView.addSubview(zoomScaleView)
previewImageView = UIImageView(frame: .zero)
previewImageView.image = previewImage
zoomScaleView.addSubview(previewImageView)
scrollView.minimumZoomScale = 1
scrollView.maximumZoomScale = 3
previewImageView.frame = CGRect(x: 0, y: 0,width: focusView.frame.width, height: focusView.frame.width)
scrollView.contentSize = CGSize(width: focusView.frame.width, height: focusView.frame.width)
scrollView.contentOffset = CGPoint(x: 0, y: 0)
previewImageView.contentMode = .scaleAspectFit
previewImageView.backgroundColor = .red
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
let scrollZoomScale = scrollView.zoomScale
if scrollZoomScale == scrollView.minimumZoomScale {
scrollView.contentInset = UIEdgeInsets.zero
return
}
if previewImageView.contentClippingRect.width <= previewImageView.contentClippingRect.height {
let difference = (previewImageView.frame.width * scrollZoomScale - previewImageView.contentClippingRect.width * scrollZoomScale)
scrollView.contentSize = CGSize(width: previewImageView.frame.width * scrollZoomScale - difference,
height: previewImageView.frame.height * scrollZoomScale)
scrollView.contentInset = UIEdgeInsets.init(
top: 0,
left: -difference/2,
bottom: (self.frame.height - focusView.frame.maxY),
right: difference/2)
} else {
let difference = (previewImageView.frame.height * scrollZoomScale - previewImageView.contentClippingRect.height * scrollZoomScale)
scrollView.contentSize = CGSize(width: previewImageView.frame.width * scrollZoomScale,
height: previewImageView.frame.height * scrollZoomScale - difference)
scrollView.contentInset = UIEdgeInsets.init(
top: -difference/2,
left: 0 ,
bottom: (self.frame.height - focusView.frame.maxY) + difference/2,
right: 0)
}
}
func cropImage() -> UIImage {
return previewImageView.image?.cgImage?.cropping(to: focusView.frame)
}
}
extension UIImageView {
var contentClippingRect: CGRect {
guard let image = image else { return bounds }
guard contentMode == .scaleAspectFit else { return bounds }
guard image.size.width > 0 && image.size.height > 0 else { return bounds }
let scale: CGFloat
if image.size.width > image.size.height {
scale = bounds.width / image.size.width
} else {
scale = bounds.height / image.size.height
}
let size = CGSize(width: image.size.width * scale, height: image.size.height * scale)
let x = (bounds.width - size.width) / 2.0
let y = (bounds.height - size.height) / 2.0
return CGRect(x: x, y: y, width: size.width, height: size.height)
}
}
[Setup]
[After zooming]
I'm struggling with this problem for long time.
Is there any solution?
Thank you.
I am looking for a solution to my problem. I have a class that creates a UIButton for me:
class MenuButton: UIButton {
var x: CGFloat
var y: CGFloat
var width: CGFloat
var height: CGFloat
required init(parent: UIView = UIView(), x: CGFloat = 0, y: CGFloat = 0, width: CGFloat = 40, height: CGFloat = 40) {
self.x = x
self.y = y
self.width = width
self.height = height
super.init(frame: .zero)
frame = CGRect(x: x, y: y, width: width, height: height)
backgroundColor = global.labelColor
setTitleColor(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1), for: .normal)
layer.cornerRadius = self.frame.height / 2
if x < 0 {
frame = CGRect(x: superview?.frame.width ?? 0 - abs(x) - frame.width, y: frame.minY, width: frame.width, height: frame.height)
}
if y < 0 {
frame = CGRect(x: frame.minX, y: superview?.frame.height ?? 0 - abs(y) - frame.height, width: frame.width, height: frame.height)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Then I add a constant that is assigned to this class:
let pauseButton = MenuButton()
This works fine and the button is shown on the scene with everything to default. Now I want to add those things in the viewDidLoad() (I added the subview to acces it in the class):
view.addSubview(pauseButton)
pauseButton.x = -20
pauseButton.y = 20
pauseButton.width = 40
pauseButton.height = 40
This does not update the stuff I specified in the init(). How do I call the init again (without doing pauseButton = MenuButton() again) or is there like an update() I can use?
Thank you for your help ;)
You can actually pass those values in the initializer as below,
let pauseButton = MenuButton(parent: view, x: -20, y: 20, width: 40, height: 40)
If you want to make your button update immediately as you change any of these properties then you can use didSet callback to set the frame as below,
class MenuButton: UIButton {
var x: CGFloat {
didSet {
self.updateFrame()
}
}
var y: CGFloat {
didSet {
self.updateFrame()
}
}
var width: CGFloat {
didSet {
self.updateFrame()
}
}
var height: CGFloat {
didSet {
self.updateFrame()
}
}
private func updateFrame() {
frame = CGRect(x: x, y: y, width: width, height: height)
}
required init(parent: UIView = UIView(), x: CGFloat = 0, y: CGFloat = 0, width: CGFloat = 40, height: CGFloat = 40) {
self.x = x
self.y = y
self.width = width
self.height = height
super.init(frame: .zero)
frame = CGRect(x: x, y: y, width: width, height: height)
backgroundColor = .green
setTitleColor(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1), for: .normal)
layer.cornerRadius = self.frame.height / 2
if x < 0 {
frame = CGRect(x: superview?.frame.width ?? 0 - abs(x) - frame.width, y: frame.minY, width: frame.width, height: frame.height)
}
if y < 0 {
frame = CGRect(x: frame.minX, y: superview?.frame.height ?? 0 - abs(y) - frame.height, width: frame.width, height: frame.height)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
each UIViewController have UIView accessed inside of it as view
you are adding the subview already here.
view.addSubview(pauseButton)
so just pass it into the init() you have and add it there.
your code could be something like this.
class MenuButton: UIButton {
var x: CGFloat
var y: CGFloat
var width: CGFloat
var height: CGFloat
required init(parent: UIView = UIView(), x: CGFloat = 0, y: CGFloat = 0, width: CGFloat = 40, height: CGFloat = 40) {
self.x = x
self.y = y
self.width = width
self.height = height
super.init(frame: .zero)
frame = CGRect(x: x, y: y, width: width, height: height)
backgroundColor = global.labelColor
setTitleColor(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1), for: .normal)
layer.cornerRadius = self.frame.height / 2
if x < 0 {
frame = CGRect(x: superview?.frame.width ?? 0 - abs(x) - frame.width, y: frame.minY, width: frame.width, height: frame.height)
}
if y < 0 {
frame = CGRect(x: frame.minX, y: superview?.frame.height ?? 0 - abs(y) - frame.height, width: frame.width, height: frame.height)
}
parent.addSubview(self)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And use it in ViewDidLoad like this.
let _ = MenuButton(parent: view, x: -20, y: 20, width: 40, height: 40)
init() method is called only once when you instantiating your button. You should try to instantiate the button with the desired properties. Try something like this:
let pauseButton = MenuButton(parent: UIView(), x: -20, y: 20, width: 40, height:40)
Then in your viewDidLoad() method you can have just this line:
view.addSubview(pauseButton)
Another problem that I noticed is that you are referencing to the superview in your init() method, but superview is always nil in that case, since your button is not yet added as a subview.
have you tried ?
button.frame = CGRect(x:xPostion, y:yPostion, width:buttonWidth, height:buttonHeight)
or u can declare a funtion called updateFrame in ur Menu button class and pass new position as argument , like this :
func updateFrame(x:CGFloat, y: CGFloat, width: CGFloat, height: CGFloat){
self.frame = CGRect(x:xPostion, y:yPostion, width:buttonWidth, height:buttonHeight)
}
and in your VideDidLoad :
button.updateFrame(x:xPostion, y:yPostion, width:buttonWidth, height:buttonHeight)
what you are doing at the moment is just assigning some values to your Button's properties, how should your button knows to use them to reset its frame ??
I am currently messing around with IBDesignable Views, and I am curious if anyone has been able to solve this. I would like to have views added through the interface builder be automatically arranged using a custom layout algorithm within my subview. The view works great when I run the app, but in the interface builder, the views do not rearrange in real time.
I have tried debugging my UIView class, but it seems at all times when the interface builder is initializing the element, it thinks it has zero subviews. It seems the interface builder does not give you a chance to arrange these views after the fact. However, I'm wondering if maybe there is just something I'm missing. Is it possible to rearrange subviews added from the interface builder within an IBDesignable class, and have the views show up rearranged in the interface builder?
Try using the provided method for a custom view and IBDesignable if you are not already. You also might need to refresh your views in Xcode or have it automatically refresh views. Below is the function you may be missing. This is never called in a live app. It is only called in Xcode IB.
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setUpView()
}
In this instance setUpView is laying out my subviews.
Here is an example that I made. https://github.com/agibson73/ICONButton
import UIKit
#IBDesignable class AGIconButton: UIControl {
private var iconImageView : UIImageView!
private var iconLabel : UILabel!
private var mainSpacer : UIView!
private var highlightView:UIView!
private var widthContraint : NSLayoutConstraint!
var padding : CGFloat = 5
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
addTarget(self, action: #selector(AGIconButton.userDidTouchDown), for: .touchDown)
addTarget(self, action: #selector(AGIconButton.userDidTouchUp), for: .touchUpInside)
addTarget(self, action: #selector(AGIconButton.userDidTouchUpOutside), for: .touchUpOutside)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
addTarget(self, action: #selector(AGIconButton.userDidTouchDown), for: .touchDown)
addTarget(self, action: #selector(AGIconButton.userDidTouchUp), for: .touchUpInside)
addTarget(self, action: #selector(AGIconButton.userDidTouchUpOutside), for: .touchUpOutside)
}
//only called at design time
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setUpView()
addTarget(self, action: #selector(AGIconButton.userDidTouchDown), for: .touchDown)
addTarget(self, action: #selector(AGIconButton.userDidTouchUp), for: .touchUpInside)
addTarget(self, action: #selector(AGIconButton.userDidTouchUpOutside), for: .touchUpOutside)
}
#IBInspectable var iconImage: UIImage = UIImage() {
didSet {
iconImageView.image = iconImage
}
}
#IBInspectable var imageSize: CGFloat = 40 {
didSet {
setUpView()
}
}
#IBInspectable var imagePadding: CGFloat = 10 {
didSet {
setUpView()
}
}
#IBInspectable var iconText: String = "Icon Button Time" {
didSet {
setUpView()
}
}
#IBInspectable var iconTextSize: CGFloat = 15 {
didSet {
setUpView()
}
}
#IBInspectable var iconTextColor: UIColor = UIColor.black {
didSet {
setUpView()
}
}
#IBInspectable var alignment: Int = 1 {
didSet {
setUpView()
}
}
override var intrinsicContentSize: CGSize {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: iconTextSize)
label.text = iconText
label.sizeToFit()
return CGSize(width: imageSize + label.frame.width + imagePadding + (padding * 2), height: CGFloat(max(label.frame.height, imageSize) + padding * 2))
}
#IBInspectable var highLightColor: UIColor = UIColor.lightGray {
didSet {
setUpView()
}
}
#IBInspectable var shouldBounce: Bool = true
#IBInspectable var borderColor: UIColor = UIColor.clear {
didSet {
layer.borderColor = borderColor.cgColor
}
}
#IBInspectable var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
}
}
private func setUpView(){
if iconImageView == nil{
iconImageView = UIImageView(image: iconImage)
iconImageView.contentMode = .scaleAspectFit
iconImageView.isUserInteractionEnabled = false
self.addSubview(iconImageView)
}
if mainSpacer == nil{
mainSpacer = UIView(frame: CGRect(x: 0, y: 0, width: imagePadding, height: self.bounds.height))
mainSpacer.isUserInteractionEnabled = false
self.addSubview(mainSpacer)
}
if iconLabel == nil{
iconLabel = UILabel()
iconLabel.isUserInteractionEnabled = false
self.addSubview(iconLabel)
}
if highlightView == nil{
highlightView = UIView(frame: self.bounds)
highlightView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
highlightView.alpha = 0
highlightView.isUserInteractionEnabled = false
self.addSubview(highlightView)
self.bringSubview(toFront: highlightView)
}
highlightView.backgroundColor = highLightColor
iconLabel.font = UIFont.systemFont(ofSize: iconTextSize)
iconLabel.text = iconText
iconLabel.textColor = iconTextColor
iconLabel.sizeToFit()
var usedWidth : CGFloat = self.intrinsicContentSize.width
if bounds.width < usedWidth{
usedWidth = bounds.width
}
let maxImageHeight = min(self.bounds.height - padding, imageSize)
//resize iconlabel if we have to
if maxImageHeight + imagePadding + iconLabel.bounds.width + padding * 2 > usedWidth{
iconLabel.frame = CGRect(x: 0, y: 0, width: self.bounds.width - iconImageView.bounds.width - imagePadding - padding * 2, height: iconLabel.bounds.height)
iconLabel.fitFontForSize(minFontSize: 1, maxFontSize: iconTextSize, accuracy: 1.0)
}
let maxWidth = (self.bounds.width - iconLabel.bounds.width - maxImageHeight - imagePadding) / 2
switch alignment {
case 0:
//intrinsic left
iconImageView.frame = CGRect(x:padding, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
mainSpacer.frame = CGRect(x: maxImageHeight + padding, y: 0, width: imagePadding, height: self.bounds.height)
iconLabel.frame = CGRect(x: maxImageHeight + imagePadding + padding, y: 0, width: iconLabel.frame.width, height: bounds.height)
break
case 1:
//intrinsic center
iconImageView.frame = CGRect(x: maxWidth, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
mainSpacer.frame = CGRect(x: maxWidth + maxImageHeight, y: 0, width: imagePadding, height: self.bounds.height)
iconLabel.frame = CGRect(x: maxWidth + maxImageHeight + imagePadding, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
break
case 2:
//intrinsic icon right text aligned right
iconLabel.frame = CGRect(x: maxWidth, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
iconLabel.textAlignment = .right
mainSpacer.frame = CGRect(x: iconLabel.frame.width + maxWidth, y: 0, width: imagePadding, height: self.bounds.height)
iconImageView.frame = CGRect(x: iconLabel.frame.width + imagePadding + maxWidth, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
break
case 3:
//intrinsic center invert icon
iconLabel.frame = CGRect(x:maxWidth, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
mainSpacer.frame = CGRect(x: maxWidth + iconLabel.bounds.width, y: 0, width: imagePadding, height: self.bounds.height)
iconImageView.frame = CGRect(x: maxWidth + iconLabel.bounds.width + imagePadding, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
break
default:
//intrinsic center
iconImageView.frame = CGRect(x: maxWidth, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
mainSpacer.frame = CGRect(x: maxWidth + maxImageHeight, y: 0, width: imagePadding, height: self.bounds.height)
iconLabel.frame = CGRect(x: maxWidth + maxImageHeight + imagePadding, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
}
}
//layout subviews
override func layoutSubviews() {
super.layoutSubviews()
setUpView()
}
//MARK: Touch Events
//TODO: run on timer to simulate a real press
func userDidTouchDown(){
if shouldBounce == true{
animateBouncyDown()
}else{
self.animateHighlightTo(alpha: 0.3)
}
}
func userDidTouchUp(){
if shouldBounce == true{
animateBouncyUp()
}else{
self.animateHighlightTo(alpha: 0)
}
}
func userDidTouchUpOutside(){
if shouldBounce == true{
animateBouncyUp()
}else{
self.animateHighlightTo(alpha: 0)
}
}
func animateHighlightTo(alpha:CGFloat){
UIView.animate(withDuration: 0.2, animations: { [weak self] in
self?.highlightView.alpha = alpha
})
}
func animateBouncyDown(){
self.transform = CGAffineTransform.identity
UIView.animate(withDuration: 0.15, animations: { [weak self] in
self?.transform = CGAffineTransform(scaleX: 0.85, y: 0.85)
})
}
func animateBouncyUp(){
UIView.animate(withDuration: 0.2, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: .curveEaseInOut, animations: {[weak self] in
if self != nil{
self?.transform = CGAffineTransform.identity
}
}, completion: nil)
}
}
extension UILabel {
func fitFontForSize( minFontSize : CGFloat = 1.0, maxFontSize : CGFloat = 300.0, accuracy : CGFloat = 1.0) {
var maxFontSize = maxFontSize
var minFontSize = minFontSize
assert(maxFontSize > minFontSize)
layoutIfNeeded() // Can be removed at your own discretion
let constrainedSize = bounds.size
while maxFontSize - minFontSize > accuracy {
let midFontSize : CGFloat = ((minFontSize + maxFontSize) / 2)
font = font.withSize(midFontSize)
sizeToFit()
let checkSize : CGSize = bounds.size
if checkSize.height < constrainedSize.height && checkSize.width < constrainedSize.width {
minFontSize = midFontSize
} else {
maxFontSize = midFontSize
}
}
font = font.withSize(minFontSize)
sizeToFit()
layoutIfNeeded()
}
}
There doesn't seem to be any way to do this. If you drop a custom control into another xib and add subviews to the custom control in Interface Builder, those subviews appear at the same level as the custom control in the view hierarchy when you load the xib. It looks like custom controls can not act as containers in other xibs.
var view = UIView(frame: CGRect(x: 5, y: 5, width:50, height: 30))
println(view.frame.size.width)
println(view.frame.width)
They both print out the same value, whats the difference between these two? Are there some specific scenarios where a particular one should be used?
CGRect extension has width and height properties as get method.
struct CGRect {
var origin: CGPoint
var size: CGSize
}
extension CGRect {
var width: CGFloat { get }
var height: CGFloat { get }
}
Basically they are the same the difference is
frame.width = 5 is not possible
frame.size.width = 5 is possible