0

I'm trying to add 1000 UI labels on a button click but I'm looping for 1000 times and creating 1000 UI label

    @IBAction func display(_ sender: Any) {
      for i in Range(1...1000) {

                      let label =  self.displayLabel(str: "test \(i)",i: i)

                                        )
                        UIView.animate(withDuration: 2.0, delay: 1.0, options: UIView.AnimationOptions.transitionCrossDissolve,animations: {
                            self.view.addSubview(label)
                            label.alpha = 1.0;
                         label.center.y = self.view.center.y/3},
                                       completion: { (value) in label.removeFromSuperview()} )



                      }

}

And the displayLabel function is

    func displayLabel(str:String,i:Int) -> UILabel {

    let label = UILabel.init(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
    label.tag = i

    label.font = UIFont.preferredFont(forTextStyle: .footnote)
    let screenWidth = self.view.frame.size.width
    let screenHeight = self.view.frame.size.height
    label.textColor = .black
    label.center = CGPoint(x: screenWidth * 0.75, y: screenHeight - (screenHeight/3.0))
    label.textAlignment = .center
    label.text = str
    label.backgroundColor = .white
    label.backgroundColor?.withAlphaComponent(0.5)
    return label


}

Whats expected ?

the screens displays labels one after the other and move up and disappear.

Whats actually happening

All the labels are getting added at the same time and then they start animating making label number 1000 animate and disappear.

Why do you think this happens ?.

Is this because of the tag or why does it wait for all 1000 labels and add it to subview?

How can I achive what I want?.

Mahi Tej Gvp
  • 984
  • 1
  • 14
  • 34
  • Consider https://stackoverflow.com/questions/29856947/how-do-i-change-the-text-of-a-uilabel-between-each-animation-of-animatewithdurat – matt Mar 06 '20 at 13:48

2 Answers2

1

According to your code, it is the expected behavior because you are removing label object in the completion handler and it is @escaping closure.

It means, execution will not wait for removeFromSuperview. It will do addSubview operation and without waiting for completion handler, it will move on.

You can verify this behavior with adding print statements before and after completion handler.

Now, about your requirement, it can be met in following way:

Declare a variable to hold the label tag

var tag = 0

Avoid for loop and use recursive function here, like

  //function to add and remove label until the defined count reached
  func showLabel(withTag: Int) {
    if self.tag != 1000 {
      let label =  self.displayLabel(str: "test \(withTag)",i: withTag)
      UIView.animate(withDuration: 2.0,
                     delay: 1.0,
                     options: UIView.AnimationOptions.transitionCrossDissolve,
                     animations: {
                      self.view.addSubview(label)
                      label.alpha = 1.0;
                      label.center.y = self.view.center.y/3
      }) { (value) in
        label.removeFromSuperview()
        self.tag = self.tag + 1
        self.showLabel(withTag: self.tag)
      }
    }
  }

  //initial function to start the process for each label
  func display() {
    showLabel(withTag: tag)
  }

  //function to provide new label object
  //no changes here
  func displayLabel(str:String,i:Int) -> UILabel {
    let label = UILabel.init(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
    label.tag = i
    label.font = UIFont.preferredFont(forTextStyle: .footnote)
    let screenWidth = self.view.frame.size.width
    let screenHeight = self.view.frame.size.height
    label.textColor = .black
    label.center = CGPoint(x: screenWidth * 0.75, y: screenHeight - (screenHeight/3.0))
    label.textAlignment = .center
    label.text = str
    label.backgroundColor = .white
    label.backgroundColor?.withAlphaComponent(0.5)
    return label
  }
RJ168
  • 1,006
  • 2
  • 12
  • 22
  • hi, @RJ168 although this works as expected it displays everything sequentially how do I make it work asynchronously i.e I don't want 2nd label to wait for 1'st labels execution. any help on this? – Mahi Tej Gvp Mar 09 '20 at 08:26
  • @MahiTejGvp Above code will not execute in async manner.. you need to change the implementation. – RJ168 Mar 11 '20 at 12:22
0

You should layout subviews after every .addSubview. So try doing this:

UIView.animate(withDuration: 2.0, delay: 1.0, options: UIView.AnimationOptions.transitionCrossDissolve,animations: {
                        self.view.addSubview(label)
                        label.alpha = 1.0;
                        label.center.y = self.view.center.y/3
                        self.view.layoutSubviews() }