iOS - Exchanging position of two UIViews inside Animation block - ios

I've been having a strong headache with this problem, because I know for a fact should be a simple solution but I can't find the right answer.
The idea is that I have two UIViews (let's call them A and B). All I want to do is to swap their positions on button press, by the A view going down to B position, and B view going up to A position.
Here's what I'm trying to achieve.
I'm trying to achieve that by just swapping the .center of the elements that I need (as A and B are both UIViews that cointains a bunch of elements inside them). This is inside an animation block - UIView.animate
However, what is currently happening is that the animation is performing in exactly the opposite way: The A view would go up the screen, then re-appear in the bottom side of the screen and end up in the desired position (B initial position). Same thing with the B view, it's currently going all the way to the bottom of the screen, then re-appear at the top, and finish the animation in the desired position (A initial position).
This is what is currently happening.
This is my code so far (note that ownAccountTransferView is defined in another file, and below code is placed in my UIViewController that stores that view). I call this function on button press, after swapping the the data (like labels and such) between the two cards.
fileprivate func performSwitchAnimation() {
// NOTE: ownAccountTransferView is a reference to my view, think of it like self.view
let fromAccountCardCenter =
self.ownAccountTransferView.fromAccountCardView.center
let fromAccountTypeLabelCenter =
self.ownAccountTransferView.fromAccountTypeLabel.center
let fromAccountTypeViewCenter =
self.ownAccountTransferView.fromAccountTypeView.center
let fromAccountBalanceLabelCenter =
self.ownAccountTransferView.fromAccountBalanceLabel.center
let fromAccountNameLabelCenter =
self.ownAccountTransferView.fromAccountNameLabel.center
let fromAccountChevronCenter =
self.ownAccountTransferView.fromAccountChevronView.center
let toAccountCardCenter =
self.ownAccountTransferView.toAccountCardView.center
let toAccountTypeLabelCenter =
self.ownAccountTransferView.toAccountTypeLabel.center
let toAccountTypeViewCenter =
self.ownAccountTransferView.toAccountTypeView.center
let toAccountBalanceLabelCenter =
self.ownAccountTransferView.toAccountBalanceLabel.center
let toAccountNameLabelCenter =
self.ownAccountTransferView.toAccountNameLabel.center
let toAccountChevronCenter =
self.ownAccountTransferView.toAccountChevronView.center
UIView.animate(withDuration: 1, delay: 0, options: []) {
self.ownAccountTransferView.switchAccountsButton.isUserInteractionEnabled = false
self.ownAccountTransferView.fromAccountCardView.center = toAccountCardCenter
self.ownAccountTransferView.fromAccountTypeLabel.center = toAccountTypeLabelCenter
self.ownAccountTransferView.fromAccountTypeView.center = toAccountTypeViewCenter
self.ownAccountTransferView.fromAccountBalanceLabel.center = toAccountBalanceLabelCenter
self.ownAccountTransferView.fromAccountNameLabel.center = toAccountNameLabelCenter
self.ownAccountTransferView.fromAccountChevronView.center = toAccountChevronCenter
self.ownAccountTransferView.toAccountCardView.center = fromAccountCardCenter
self.ownAccountTransferView.toAccountTypeLabel.center = fromAccountTypeLabelCenter
self.ownAccountTransferView.toAccountTypeView.center = fromAccountTypeViewCenter
self.ownAccountTransferView.toAccountBalanceLabel.center = fromAccountBalanceLabelCenter
self.ownAccountTransferView.toAccountNameLabel.center = fromAccountNameLabelCenter
self.ownAccountTransferView.toAccountChevronView.center = fromAccountChevronCenter
} completion: { isDone in
if isDone {
self.ownAccountTransferView.switchAccountsButton.isUserInteractionEnabled = true
}
}
}
So, how can I make the animation work the way I want - A view going to the bottom and B view going to the top?
Thanks in advance.
UPDATE: I fixed it by ussing transform. Tried messing around with animating constraints A LOT, tried every possible interaction between top/bottom constraints, but it would either not do anything at all or just bug my view and send it to hell. I even was insisting with animating constraints in support calls with my coworkers. And so were they.
However, after googling despair measures, I ended up using viewController.myView.myLabel.transform = CGAffineTransform(translationX: 0, y: 236).
Of course, you can reduce this to: view.transform = CGAffineTransform(translationX: 0, y: 236).
The value is arbitrary, and I'm not sure if it can some day break or cause undesired behaviour. I tested it on iPhone 8 and iPhone 13, and in both the animations performs just ok (A view goes down, B goes up, click switch and do the opposite, etc...).
Side note: If you gonna use this, you need to NOT copy/paste the values when you need your views to "come back" to where they were at the beginning. I used 236 and -236 for A and B respectively, but to restore them I need to use -2 and 2 or it super bugs out. Idk why tho.
But it works!
Thanks all

Enclose both the views in a parent view and do the following:
if let firstIndex = view.subviews.firstIndex(of: view1), let secondIndex = view.subviews.firstIndex(of: view2) {
let firstViewFrame = view1.frame
UIView.animate(withDuration: 1, animations: {
self.view1.frame = self.view2.frame
self.view2.frame = firstViewFrame
self.view.exchangeSubview(at: firstIndex, withSubviewAt: secondIndex)
})
}
Check out the answer here.
EDIT: I was able to achieve the said animation using Autolayout constraints. Basically what I did was have fixed constraints for two views initially. And have two sets of changing constraints - startConstraints and endConstraints. Simply activating and deactivating the constraints did the job. Check out the demo. Here's my code:
class ViewController: UIViewController {
var view1: UIView = {
let v = UIView()
v.backgroundColor = .red
return v
}()
var view2: UIView = {
let v = UIView()
v.backgroundColor = .blue
return v
}()
var button: UIButton = {
let b = UIButton()
b.setTitle("Animate", for: .normal)
b.setTitleColor(.black, for: .normal)
return b
}()
var fixedConstraints: [NSLayoutConstraint]!
var startConstraints: [NSLayoutConstraint]!
var endConstraints: [NSLayoutConstraint]!
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(view1)
view.addSubview(view2)
view.addSubview(button)
view1.translatesAutoresizingMaskIntoConstraints = false
view2.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
fixedConstraints = [
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
view1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
view2.centerXAnchor.constraint(equalTo: view.centerXAnchor),
view1.heightAnchor.constraint(equalToConstant: 100),
view1.widthAnchor.constraint(equalToConstant: 100),
view2.heightAnchor.constraint(equalToConstant: 100),
view2.widthAnchor.constraint(equalToConstant: 100),
]
startConstraints = [
view1.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view2.bottomAnchor),
]
startConstraints.append(contentsOf: fixedConstraints)
NSLayoutConstraint.activate(startConstraints)
button.addTarget(self, action: #selector(animateSwitchingOfViews), for: .touchUpInside)
}
#objc
private func animateSwitchingOfViews(){
endConstraints = [
view2.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo:view1.bottomAnchor)
]
endConstraints.append(contentsOf: fixedConstraints)
UIView.animate(withDuration: 2, animations: {
NSLayoutConstraint.deactivate(self.startConstraints)
NSLayoutConstraint.activate(self.endConstraints)
self.view.layoutIfNeeded()
})
}
}

UPDATE: I fixed it by ussing transform. Tried messing around with animating constraints A LOT, tried every possible interaction between top/bottom constraints, but it would either not do anything at all or just bug my view and send it to hell. I even was insisting with animating constraints in support calls with my coworkers. And so were they. However, after googling despair measures, I ended up using viewController.myView.myLabel.transform = CGAffineTransform(translationX: 0, y: 236).
Of course, you can reduce this to: view.transform = CGAffineTransform(translationX: 0, y: 236). The value is arbitrary, and I'm not sure if it can some day break or cause undesired behaviour. I tested it on iPhone 8 and iPhone 13, and in both the animations performs just ok (A view goes down, B goes up, click switch and do the opposite, etc...).
Side note: If you gonna use this, you need to NOT copy/paste the values when you need your views to "come back" to where they were at the beginning. I used 236 and -236 for A and B respectively, but to restore them I need to use -2 and 2 or it super bugs out. Idk why tho. But it works! Thanks all

Assuming you want to "swap the centers" and you don't want to modify the constraints / constants, you can use transforms.
Instead of hard-coding values, though, you can do it like this:
// if the views are in their "original" positions
if ownAccountTransferView.transform == .identity {
let xOffset = fromAccountTypeView.center.x - ownAccountTransferView.center.x
let yOffset = fromAccountTypeView.center.y - ownAccountTransferView.center.y
UIView.animate(withDuration: 1.0, animations: {
self.ownAccountTransferView.transform = CGAffineTransform(translationX: xOffset, y: yOffset)
self.fromAccountTypeView.transform = CGAffineTransform(translationX: -xOffset, y: -yOffset)
})
} else {
UIView.animate(withDuration: 1.0, animations: {
self.ownAccountTransferView.transform = .identity
self.fromAccountTypeView.transform = .identity
})
}

Related

UIView animating to wrong position after being scaled while using constraints

So I'm trying to animate an UILabel to match another UILabel size and position. At first I was only working with the animation of the position part using constraints like this:
private lazy var constraintsForStateA: [NSLayoutConstraint] = [
firstLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
firstLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor)
]
private lazy var constraintsForStateB: [NSLayoutConstraint] = [
firstLabel.leadingAnchor.constraint(equalTo: secondLabel.leadingAnchor),
firstLabel.topAnchor.constraint(equalTo: secondLabel.topAnchor)
]
So I basically have two arrays with these constraints above (constraintsForStateA and constraintsForStateB), and when I need to animate the position of my firstLabel I just do something like:
// From state A to state B
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) {
NSLayoutConstraint.deactivate(self._constraintsUnselected)
NSLayoutConstraint.activate(self._constraintsSelected)
self.layoutIfNeeded()
} completion: { _ in
self.firstLabel.alpha = 0
self.secondLabel.alpha = 1
}
}
So far this has been working exactly as I was expecting, but the part with the size is giving me some trouble, since I can't apply the same strategy to the text size I'm using a transform like this:
let scaleX = secondLabel.bounds.width / firstLabel.bounds.width
let scaleY = secondLabel.bounds.height / firstLabel.bounds.height
and then in the same .animate method above from state A to state B I do this:
firstLabel.transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
and from state B to state A:
firstLabel.transform = .identity
Which works as I want but the problem I'm facing is that the position is no longer in the expected place. I think this is happening because the transformation is happening having in consideration the anchorPoint at the center of the label. I've tried sort of blindly making it work changing the anchorPoint to be at (0,0), but it's not working either. I've lost 2 days on this already, any help or guidance is welcome!
When you animate, it would be much simpler if you just forget about the constraints and just deal with frames:
NSLayoutConstraint.deactivate(self.constraintsForStateA)
firstLabel.translatesAutoresizingMaskIntoConstraints = true
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) {
firstLabel.frame = secondLabel.frame
}
Then to transition from state B to A:
firstLabel.translatesAutoresizingMaskIntoConstraints = false
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) {
NSLayoutConstraint.activate(self.constraintsForStateA)
self.layoutIfNeeded()
}

How to Apply Animation while changing UIView size? + Swift 5

Im new to IOS development and I have two buttons in the UIView and when user select the option portrait or landscape, change the UIView re-size and change the background color as well and i need to add animation for that process.
As as ex:
User select portrait and then user can see red color UIVIew. after click the landscape option, animation should be started and it looks like, red color image come front and change the size (changing height and width) for landscape mode and go to previous position and change color to green. i have added small UIView animation on code and it is helpful to you identify the where should we start the animation and finish it. if someone know how to it properly, please, let me know and appreciate your help. please, refer below code
import UIKit
class ViewController: UIViewController {
let portraitWidth : CGFloat = 400
let portraitHeight : CGFloat = 500
let landscapeWidth : CGFloat = 700
let landscapeHeight : CGFloat = 400
var mainView: UIView!
var mainStackView: UIStackView!
let segment: UISegmentedControl = {
let segementControl = UISegmentedControl()
return segementControl
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.translatesAutoresizingMaskIntoConstraints = false
mainStackView = UIStackView()
mainStackView.axis = .vertical
mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.alignment = .center
mainStackView.distribution = .equalCentering
self.view.addSubview(mainStackView)
self.segment.insertSegment(withTitle: "Portrait", at: 0, animated: false)
self.segment.insertSegment(withTitle: "Landscape", at: 1, animated: false)
self.segment.selectedSegmentIndex = 0
self.segment.addTarget(self, action: #selector(changeOrientation(_:)), for: .valueChanged)
self.segment.translatesAutoresizingMaskIntoConstraints = false
self.segment.selectedSegmentIndex = 0
mainStackView.addArrangedSubview(self.segment)
let safeAreaLayoutGuide = self.view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
self.mainStackView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 20),
self.mainStackView.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor, constant: 0),
])
NSLayoutConstraint.activate([
self.segment.heightAnchor.constraint(equalToConstant: 35),
self.segment.widthAnchor.constraint(equalToConstant: 300)
])
mainView = UIView(frame: .zero)
mainView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.addArrangedSubview(mainView)
mainView.backgroundColor = UIColor.red
NSLayoutConstraint.activate([
mainView.heightAnchor.constraint(equalToConstant: portraitHeight),
mainView.widthAnchor.constraint(equalToConstant: portraitWidth),
mainView.topAnchor.constraint(equalTo: segment.bottomAnchor, constant: 30)
])
}
#IBAction func changeOrientation(_ sender: UISegmentedControl) {
self.mainView.constraints.forEach{ (constraint) in
self.mainView.removeConstraint(constraint)
}
UIView.animate(withDuration: 1.0) {
if (sender.selectedSegmentIndex == 0) {
self.mainView.backgroundColor = UIColor.red
NSLayoutConstraint.activate([
self.mainView.heightAnchor.constraint(equalToConstant: self.portraitHeight),
self.mainView.widthAnchor.constraint(equalToConstant: self.portraitWidth),
self.mainView.topAnchor.constraint(equalTo: self.segment.bottomAnchor, constant: 30)
])
} else {
self.mainView.backgroundColor = UIColor.green
NSLayoutConstraint.activate([
self.mainView.heightAnchor.constraint(equalToConstant: self.landscapeHeight),
self.mainView.widthAnchor.constraint(equalToConstant: self.landscapeWidth),
self.mainView.topAnchor.constraint(equalTo: self.segment.bottomAnchor, constant: 30)
])
}
}
}
}
updated logic
#IBAction func changeOrientation(_ sender: UISegmentedControl) {
UIView.animate(withDuration: 4.0) {
if (sender.selectedSegmentIndex == 0) {
self.mainView.backgroundColor = UIColor.red
self.widthConstraint.constant = self.portraitWidth
self.heightConstraint.constant = self.portraitWidth
} else {
self.mainView.backgroundColor = UIColor.green
self.widthConstraint.constant = self.landscapeWidth
self.heightConstraint.constant = self.landscapeHeight
}
self.mainView.layoutIfNeeded()
} }
It's possible, the basic problem is:
• It's not possible to animate an actual change between one constraint and another.
To change size or shape you simply animate the length of a constraint.
While you are learning I would truly urge you to simply animate the constraints.
So, don't try to "change" constraints, simply animate the length of one or the other.
yourConstraint.constant = 99 // it's that easy
I also truly urge you to just lay it out on storyboard.
You must master that first!
Also there is no reason at all for the stack view, get rid of it for now.
Just have an outlet
#IBOutlet var yourConstraint: NSLayoutConstraint!
and animate
UIView.animateWithDuration(4.0) {
self.yourConstraint.constant = 666
self.view.layoutIfNeeded()
}
Be sure to search in Stack Overflow for "iOS animate constraints" as there is a great deal to learn.
Do this on storyboard, it will take 20 seconds tops for such a simple thing. Once you have mastered storyboard, only then move on to code if there is some reason to do so
Forget the stack view
Simply animate the constant of the constraint(s) to change the size, shape or anything you like.
Be sure to google for the many great articles "animating constraints in iOS" both here on SO and other sites!

Not able to lay out Subviews on a custom UIView in Swift

So I have created this custom container view which I am laying out using autolayout constraint.
func configureSegmentContainerView() {
segmentContainerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 40).isActive = true
segmentContainerView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8).isActive = true
segmentContainerView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8).isActive = true
segmentContainerView.heightAnchor.constraint(equalToConstant: 3).isActive = true
}
In the view controller, the viewDidLoad() is this:
setupDataSource()
segmentContainerView = ATCStorySegmentsView()
view.addSubview(segmentContainerView)
configureSegmentContainerView()
segmentContainerView.translatesAutoresizingMaskIntoConstraints = false
segmentContainerView.numberOfSegments = friendStory.count
Now once the data source is setup and I have the friendStory count, I am assigning it to segmentContainerView.numberofSegments
In segmentContainerview class this is what is happening:
var numberOfSegments: Int? {
didSet {
addSegments()
}
}
In addSegments(), I am adding UIViews depending upon the numberOfSegments this is the code for that:
private func addSegments() {
guard let numberOfSegments = numberOfSegments else { return }
layoutIfNeeded()
setNeedsLayout()
for i in 0..<numberOfSegments {
let segment = Segment()
addSubview(segment.bottomSegment)
addSubview(segment.topSegment)
configureSegmentFrame(index: i, segmentView: segment)
segmentsArray.append(segment)
}
}
private func configureSegmentFrame(index: Int, segmentView: Segment) {
guard let numberOfSegments = numberOfSegments else { return }
let widthOfSegment : CGFloat = (self.frame.width - (padding * CGFloat(numberOfSegments - 1))) / CGFloat(numberOfSegments)
let i = CGFloat(index)
let segmentFrame = CGRect(x: i * (widthOfSegment + padding), y: 0, width: widthOfSegment, height: self.frame.height)
segmentView.bottomSegment.frame = segmentFrame
segmentView.topSegment.frame = segmentFrame
segmentView.topSegment.frame.size.width = 0
}
**Question and Issue: ** Instead of getting 4 UIViews, I am getting 3, but the third one is not correctly placed and is going outside the parent container. How can I get these uiviews aligned correctly. I am guessing there is some issue with where setNeedsLayout() and layoutIfNeeded() needs to be called. Please help.
Segment is a struct with two properties - bottomSegment and topSegment. Both being UIView
You can see how just three UIView segments appear. I needs to 4 (numberOfSegments = 4) of these. Also I am giving the parent container constant of 8 and -8 for right and leftAnchor. so all 4 segments need to be placed within this view. As you can see in the picture above the last segment is going outside of the parent container.
Try to call addSegments at onViewDidAppear. If it works, it means that in your code the view does not still have the correct frame.
For this to work you need to adapt the frames of the views, when the view controller's view changed:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let numberOfSegments = numberOfSegments else { return }
for i in 0..<numberOfSegments {
configureSegmentFrame(index: i, segmentView: segment)
}
}
You will probably have to do it cleaner, but it should work.
The problem is that
let widthOfSegment : CGFloat = (self.frame.width ...
is being called too soon. You cannot do anything in that depends upon self having its frame until after it has its real frame.
For a UIView, that moment is after layoutSubviews has been called for the first time.

Animate multiple UIViews alpha

I have multiple labels placed on scroll view like:
private func drawDates(_ multiplier: Double){
contentView.subviews.forEach{it in
it.removeFromSuperview()
}
let maximumVisibleDates = 6.0
let dateLabelOffset = Double(UIScreen.main.bounds.size.width / CGFloat(maximumVisibleDates))
let doublePoints = data.findClosestDatePointsForInterval(interval: dateLabelOffset,
multiplier: multiplier)
print(doublePoints)
let datesToShow = data.datesForPoints(points: doublePoints, multiplier: multiplier)
for (index, value) in datesToShow.enumerated(){
if index == datesToShow.count - 1 {
continue
}
let label = UILabel.init(frame: CGRect.init(x: CGFloat(doublePoints[index]),
y: Dimensions.chartHeight.value + Offsets.small.value,
width: Dimensions.dateLabelWidth.value,
height: Dimensions.dateLabelHeight.value))
label.text = redableString(value)
label.font = UIFont.systemFont(ofSize: FontSizes.yLabel.value)
label.textColor = Settings.isLightTheme() ? Colors.LightTheme.textGray.value : Colors.DarkTheme.textGray.value
label.sizeToFit()
contentView.addSubview(label)
}
}
When i call:
contentView.subviews.forEach{it in
it.removeFromSuperview()
}
Actually i want to fade "old" labels from old function call and then add new labels animated. How to do that?
There are 3 ways to remove old view and add new view with animation.
Solution 1:
Set new view alpha to 0 and add it to super view.
Do animation.
Remove old view after animation.
In this case, use for loop in animations block to set alpha, and in completion block to remove old views.
let viewToAdd = ...
viewToAdd.alpha = 0
contentView.addSubview(viewToAdd)
UIView.animate(withDuration: 1, animations: {
viewToRemove.alpha = 0
viewToAdd.alpha = 1
}, completion: { (_) in
viewToRemove.removeFromSuperview()
})
Solution 2:
Call transition(with:duration:options:animations:completion:) method. Use .transitionCrossDissolve as options value.
In this case, use for loop in animations block to remove old views and add new views.
UIView.transition(with: contentView,
duration: 1,
options: .transitionCrossDissolve,
animations: {
viewToRemove.removeFromSuperview()
contentView.addSubview(viewToAdd)
}, completion: nil)
Solution 3:
If the number of old views is equal to the number of new views, call transition(from:to:duration:options:completion:) method. Use .transitionCrossDissolve as options value.
In this case, use for loop to call the method.
UIView.transition(from: viewToRemove,
to: viewToAdd,
duration: 1,
options: .transitionCrossDissolve,
completion: nil)
You could leverage the UIView animation API:
https://developer.apple.com/documentation/uikit/uiview/1622515-animatewithduration

How to make UIView's width increase when a upload percentage increases? SWIFT 4

I have a project that uploads multiple images to Firebase, and have made a constant that calculates the percentage of the current upload progress:
uploadTask.observe(.progress, handler: { (snapshot) in
guard let progress = snapshot.progress else {
return
}
let percentage = (Float(progress.completedUnitCount) / Float(progress.totalUnitCount))
progressBlock(Double(percentage))
})
I have made a UIView element but have not connected it. I am trying to make the UIView acts as a progress bar for the user to visibly see how the upload is progressing. I have been trying to do this but have been unsuccessful at it. How can I do this? By the way: The UIView should be increasing as the percentage increases, and at 100% the UIView will reset and hide.
Thank you so much!
Create a custom subclass of UIView. Give the custom subclass a property percentComplete.
Have the view use a CAShapeLayer to draw a filled area that expands to fill the view as the percentage value increases from 0.0 to 1.0. (You could also override the drawRect() method and use Quartz drawing, but shape layers are easier and more performant.
Do some searching on CAShapeLayer for ideas on how to do that.
You can either have your view add a shape layer as an additional layer on the view, or you can have your view provide a layerClass property that causes the view's content layer to be a single CAShapeLayer
Here is the code to change width of your view dynamically.
import UIKit
class ViewController: UIViewController {
var widthAnchor:NSLayoutConstraint!
let perecentageView:UIView={
let view = UIView()
view.backgroundColor = .red
view.translatesAutoresizingMaskIntoConstraints = false//Its needed becasue we are using anchors
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(perecentageView)
perecentageView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
perecentageView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10).isActive = true
perecentageView.heightAnchor.constraint(equalToConstant: 50).isActive = true
widthAnchor = perecentageView.widthAnchor.constraint(equalToConstant: 0) // By default its 0
widthAnchor.isActive = true
self.uploadProgess()
}
func uploadProgess(){
uploadTask.observe(.progress, handler: { (snapshot) in
guard let progress = snapshot.progress else {
return
}
let percentage = (Float(progress.completedUnitCount) / Float(progress.totalUnitCount))
let newWidth = self.view.frame.width * percentage / 100
DispatchQueue.main.async(execute: {
self.widthAnchor.constant = newWidth
// self.view.frame.width is the parent view (And max width will be = self.view.frame.width)
//if you are using view from storybard with aoutolayout then just take IBOutlet of view's with and here widthAnchor = your IBOutlet
/*
if you are using view from storybard and without autolayout then use below code
self.perecentageView.frame = CGRect(x: self.perecentageView.frame.origin.x, y: self.perecentageView.frame.origin.y, width: newWidth, height: self.perecentageView.frame.height)
*/
})
})
}
}

Resources