3

I'm not using storyboard and I'm doing everything programmatically.

So my problem is that when try to run the app on my phone (This does not happen when I use the simulator in Xcode), and when I use the following method...

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    let destinationVC = ThirdViewController()
    destinationVC.selectedExercise = exercises![indexPath.row]

    self.navigationController?.pushViewController(destinationVC, animated: true)
}

...to transition from a UITableView to a UIView, the contents of the View transition normally, but the background freezes for a second before transitioning to the UIView.

What's causing this and how can I fix this?

Here is the code for the viewController that I'm transitioning to.

import UIKit
import RealmSwift

class ThirdViewController: UIViewController, UITextViewDelegate {

    let realm = try! Realm()

    var stats : Results<WeightSetsReps>?

    var weightTextField = UITextField()
    var weightLabel = UILabel()

    var notesTextView = UITextView()

    var repsTextField = UITextField()
    var repsLabel = UILabel()

    var timerImage = UIImageView()

    var nextSet = UIButton()
    var nextExcersise = UIButton()

    var selectedExercise : Exercises? {
        didSet{
            loadWsr()
        }
    }


    //MARK: - ViewDidLoad()
    override func viewDidLoad() {
        super.viewDidLoad()

        notesTextView.delegate = self

        timeClock()
        navConAcc()
        labelConfig()
        setTextFieldConstraints()
        setImageViewConstraints()
        setTextViewConstraints()
        setButtonConstraints()

        let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        view.addGestureRecognizer(tap)
    }


    //MARK: - UILabel
    func labelConfig(){
        weightTextField.placeholder = "Total weight..."
        weightTextField.layer.borderWidth = 1
        weightTextField.backgroundColor = .white
        weightTextField.layer.cornerRadius = 25
        weightTextField.layer.borderColor = UIColor.lightGray.cgColor

        weightLabel.text = "  Weight (lbs): "
        weightLabel.textColor = .black

        weightTextField.leftView = weightLabel
        weightTextField.leftViewMode = .always


        repsTextField.placeholder = "Number of Reps..."
        repsTextField.layer.borderWidth = 1
        repsTextField.backgroundColor = .white
        repsTextField.layer.cornerRadius = 25
        repsTextField.layer.borderColor = UIColor.lightGray.cgColor


        repsLabel.text = "  Repetitions: "
        repsLabel.textColor = .black

        notesTextView.layer.borderWidth = 1
        notesTextView.backgroundColor = .white
        notesTextView.layer.cornerRadius = 25
        notesTextView.layer.borderColor = UIColor.lightGray.cgColor
        notesTextView.text = "  Notes..."
        notesTextView.textColor = UIColor.lightGray
        notesTextView.returnKeyType = .done


        repsTextField.leftView = repsLabel
        repsTextField.leftViewMode = .always

        nextSet.layer.borderWidth = 1
        nextSet.backgroundColor = .white
        nextSet.layer.cornerRadius = 25
        nextSet.layer.borderColor = UIColor.lightGray.cgColor
        nextSet.setTitle("Next Set", for: .normal)
        nextSet.setTitleColor(.black, for: .normal)
        nextSet.addTarget(self, action: #selector(addNewSet), for: .touchUpInside)

        nextExcersise.layer.borderWidth = 1
        nextExcersise.backgroundColor = .white
        nextExcersise.layer.cornerRadius = 25
        nextExcersise.layer.borderColor = UIColor.lightGray.cgColor
        nextExcersise.setTitle("Next Exercise", for: .normal)
        nextExcersise.setTitleColor(.black, for: .normal)

        [weightTextField, repsTextField, notesTextView, nextSet, nextExcersise].forEach{view.addSubview($0)}
    }


    //MARK: - TextView Delegates
    func textViewDidBeginEditing(_ textView: UITextView) {
        if textView.text == "  Notes..." {
            textView.text = ""
            textView.textColor = UIColor.black
        }
    }

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if text == "\n" {
            textView.resignFirstResponder()
        }
        return true
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        if textView.text == ""{
            notesTextView.text = "  Notes..."
            notesTextView.layer.borderColor = UIColor.lightGray.cgColor
        }
    }

    //MARK: - Dismiss Keyboard Function
    @objc func dismissKeyboard(){
        view.endEditing(true)
    }


    //MARK: - TextField Constraints
    func setTextFieldConstraints(){
        weightTextField.anchor(top: view.safeAreaLayoutGuide.topAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor,padding: .init(top: 20, left: 40, bottom: 0, right: -40), size: .init(width: 0, height: 50))
        repsTextField.anchor(top: weightTextField.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 30, left: 40, bottom: 0, right: -40) ,size: .init(width: 0, height: 50))
    }


    //MARK: - UIButton Functions
    @objc func addNewSet(){
        print("It Works")
    }

    //MARK: - UIButton Constraints
    func setButtonConstraints(){
        nextSet.anchor(top: nil, leading: view.leadingAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, trailing: nil, padding: .init(top: 0, left: 40, bottom: 0, right: -150), size: .init(width: 120, height: 70))
        nextExcersise.anchor(top: nil, leading: nextSet.trailingAnchor, bottom: nextSet.bottomAnchor, trailing: view.trailingAnchor, padding: .init(top: 0, left: 85, bottom: 0, right: -40), size: .init(width: 120, height: 70))
       }


    //MARK: - ImageView Constraints
    func setImageViewConstraints(){
        timerImage.anchor(top: repsTextField.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 40, left: 0, bottom: 0, right: 0), size: .init(width: 100, height: 100))
    }

    //MARK: - TextView Constraints
    func setTextViewConstraints(){
        notesTextView.anchor(top: timerImage.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 40, left: 40, bottom: 0, right: -40), size: .init(width: 100, height: 110))
    }


    //MARK: - Navigation Bar Setup
    func navConAcc(){
        navigationItem.title = selectedExercise?.exerciseName
        navigationController?.navigationBar.prefersLargeTitles = true
    }

    //MARK: - Stopwatch
    func timeClock(){
        let image1 = UIImage(named: "stopwatch")
        timerImage = UIImageView(image: image1)
        timerImage.contentMode = .scaleAspectFit

        self.view.addSubview(timerImage)
    }

    //MARK: - Load Data
    func loadWsr() {
        stats = selectedExercise?.wsr.sorted(byKeyPath: "sets", ascending: true)
    }

    //MARK: - Save Data
    func save(wsr : WeightSetsReps){
        do {
            try realm.write {
                realm.add(wsr)
            }
        } catch {
            print("Error saving wsr data \(error)")
        }
    }

}

extension UIView {
    func anchor(top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero){

        translatesAutoresizingMaskIntoConstraints = false

        if let top = top {
            topAnchor.constraint(equalTo: top, constant: padding.top).isActive = true
        }

        if let leading = leading {
            leadingAnchor.constraint(equalTo: leading, constant: padding.left).isActive = true
        }

        if let bottom = bottom {
            bottomAnchor.constraint(equalTo: bottom, constant: padding.bottom).isActive = true
        }

        if let trailing = trailing {
            trailingAnchor.constraint(equalTo: trailing, constant: padding.right).isActive = true
        }

        if size.width != 0 {
            widthAnchor.constraint(equalToConstant: size.width).isActive = true
        }

        if size.height != 0 {
            heightAnchor.constraint(equalToConstant: size.height).isActive = true
        }
    }
}

enter image description here

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • Have you tried profiling the transition when run on your phone. Gut feeling is the delay you are seeing is something in viewDidLoad or viewWillAppear that is taking too much time and causing main thread to be blocked. – robowen5mac Jan 11 '20 at 22:41
  • Here is my viewDidLoad override func viewDidLoad() { super.viewDidLoad() notesTextView.delegate = self timeClock() navConAcc() labelConfig() setTextFieldConstraints() setImageViewConstraints() setTextViewConstraints() setButtonConstraints() let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) view.addGestureRecognizer(tap) } –  Jan 11 '20 at 23:40
  • Edit the question and put the code there please – Lou Franco Jan 11 '20 at 23:40
  • You need to profile -- we don't know what all of those functions do. If there's anything you can do later or in the background, move it out of viewDidLoad – Lou Franco Jan 11 '20 at 23:41
  • Google xcode instruments time profiler – Lou Franco Jan 11 '20 at 23:43
  • You don't even need to use instruments, just remove every function and reinsert them one at a time until you find the culprit. Then do the same with the culprit. – trndjc Jan 11 '20 at 23:55
  • @bsod I removed all the functions and I still get the same delayed behavior. I added the code to my main question.for the viewController that I'm transitioning to. –  Jan 12 '20 at 00:03
  • @RapsIn4 can run in main queue while pushViewController or u can check https://stackoverflow.com/questions/36503224/ios-app-freezes-on-pushviewcontroller/36637556#36637556 might solve u problem . my guess is some code not running in mainThread. – TheCodeTalker Jan 12 '20 at 07:46

1 Answers1

0

You're not implementing programmatic view controllers correctly. A programmatically-created view controller does all of its view building in loadView(), not viewDidLoad(). Therefore, add all of the view controller's subviews in loadView() (without calling super.loadView()). Then use viewDidLoad() (with calling super.viewDidLoad()) to do post-view work, like adding timers, notification observers, etc. I suspect the lagging is caused by an incorrectly-configured lifecycle.

It also appears you're relatively new to iOS or Swift development and so I would strongly suggest you do not use extensions, especially on UIView for auto layout. Learn how it all works first before you begin extending things. The process for programmatic auto layout is:

// adjust parameters first, like color, delegate, etc.
someView.translatesAutoresizingMaskIntoConstraints = false // set resizing to false before adding as a subview
view.addSubview(someView) // add as a subview
someView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true // add constraints
trndjc
  • 11,654
  • 3
  • 38
  • 51
  • I appreciate the suggestions, and I tried setting it up that way, but to no avail, but I'll keep searching. In regards to the extension that I have setup, I believe that I have the order setup correctly, no? Do you mind showing me exactly what you're referring to when you say that I have the order wrong? Yes, I'm relatively new to iOS and Swift, so I appreciate the help. –  Jan 12 '20 at 18:13
  • Then the problem isn't with this view controller. If you've refactored it with the correct lifecycle and ripped everything out to bare metal and it still glitches, then none of us can solve your problem without more information. As to the extension, get rid of it; just add the constraints normally per subview. If you've extended `UIView` to make auto layout easier and you have `if size.width != 0` in there somewhere, you're not doing it right. Don't make the mistake of learning too quickly. – trndjc Jan 12 '20 at 18:19