Kiran Lim
Kiran Lim

Reputation: 39

Accessing variable from another class gives 0 swift

So I want to access this variable squatCount from another class called PoseEstimator and print it out in a UILabel in another viewController.But for some reason when I do that, the label prints out the value of squatCount as zero even though the variable clearly increased.

I've read online and googled for hours the forums that I read through talking about implementing some global variable or singleton or some enum thingy I don't really understand it quite new to swift. So I'm trying to find out why this happens and solve it. Help is greatly appreciated.

PoseEstimator Code:

import AVFoundation
import Vision
import Combine
import SwiftUI

//declaring PoseEstimator as a class. We need to add AVCaptureVideoDataOutputSampleBufferDelegate and make it an obserable object to use PoseEsimation on our AVFoundation video output
class PoseEstimator: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, ObservableObject {
    let sequenceHandler = VNSequenceRequestHandler()
    @Published var bodyParts = [VNHumanBodyPoseObservation.JointName : VNRecognizedPoint]() //declare body parts as a published variable storing the coordinates of the body joints from Vision
    
    var seconds = 0 //This variable will hold a starting value of seconds. It could be any amount above 0.
    
    var timer = Timer()
    
    var isTimerRunning = false//This will be used to make sure only one timer is created at a time.
    
    
    var elaspedTime = Float()//we need it to declare it as a float because we would be storing numbers with decimals inside//This one is for Planks the one above is to fix a lunge bug
    var halfburpee = 0
    var halfLunge = 0
    var halfPU = 0
    var uprightLunge = false
    var wasInBottomPosition = false       //declare the position of the player as top or bottom
    var wasInBottomLunge = false
    var wasInTopJJ = false
    var wasInBottomPU = false
    var wasInDeadLift = false
    var wasInMidBurpee = false
    var wasInBottomBurpee = false
    var wasinupright = false
    var wasinalmostdoneBurpee = false
    var down = false
    var cooldown = true
    
    
    @Published var squatCount = 0          //declare variables for no of exercises done
    @Published var LungeCounter = 0
    @Published var JumpingJackCounter = 0
    @Published var PushupCounter = 0
    @Published var BurpeesCounter = 0
    @Published var SLDeadliftsCounter = 0
    @Published var caloriesBurntSquats = Float(0)
    @Published var caloriesBurntLunges = Float(0)
    @Published var caloriesBurntJJ = Float()
    @Published var caloriesBurntPushups = Float(0)
    @Published var caloriesBurntBurpees = Float(0)
    @Published var caloriesBurntSLD = Float(0)
    @Published var isGoodSquatPosture = true
    @Published var isGoodLungePosture = true
    @Published var isGoodJJPosture = true
    @Published var isGoodPUPosture = true
    @Published var isGoodSLDPosture = true
    
    var subscriptions = Set<AnyCancellable>()        //for combine to store the proessed coordinates of Vision
    
    func runTimer() {
         timer = Timer.scheduledTimer(timeInterval: 1, target: self,   selector: (#selector(PoseEstimator.updateTimer)), userInfo: nil, repeats: true)
    }
    
    @objc func updateTimer() {
        seconds += 1     //This will decrement(count down)the seconds.
        if seconds >= 2{
            self.cooldown = false
        }else{
            self.cooldown = true
        }
    }
    
    override init() {
        super.init()
        $bodyParts
            .dropFirst()
            .sink(receiveValue: { bodyParts in self.Process(bodyParts: bodyParts)})
            .store(in: &subscriptions)
    }
    
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        let humanBodyRequest = VNDetectHumanBodyPoseRequest(completionHandler: detectedBodyPose)
        do {
            try sequenceHandler.perform(            //sent request to detect human body joints from AVFoundation
              [humanBodyRequest],
              on: sampleBuffer,
                orientation: .right)
        } catch {
          print(error.localizedDescription)
        }
    }
    func detectedBodyPose(request: VNRequest, error: Error?) {
        guard let bodyPoseResults = request.results as? [VNHumanBodyPoseObservation]       //set bodyPoseResults as the the reuslts from Vision
          else { return }
        guard let bodyParts = try? bodyPoseResults.first?.recognizedPoints(.all) else { return }
        DispatchQueue.main.async {
            self.bodyParts = bodyParts            //set bodyparts as the recognised points from vision
        }
    }
    
    func Process(bodyParts: [VNHumanBodyPoseObservation.JointName : VNRecognizedPoint]) { // function for processing results from vision
        
        
        let rightKnee = bodyParts[.rightKnee]!.location  // declare variables that we need to determine if the player is doing exercise
        let leftKnee = bodyParts[.leftKnee]!.location
        let rightHip = bodyParts[.rightHip]!.location
        let rightAnkle = bodyParts[.rightAnkle]!.location
        let leftAnkle = bodyParts[.leftAnkle]!.location
        let leftHip = bodyParts[.leftHip]!.location
        let rightShoulder = bodyParts[.rightShoulder]!.location
        let leftShoulder = bodyParts[.leftShoulder]!.location
        let rightElbow = bodyParts[.rightElbow]!.location
        let rightWrist = bodyParts[.rightWrist]!.location
        let leftWrist = bodyParts[.leftWrist]!.location
        let root = bodyParts[.root]!.location
        let neck = bodyParts[.neck]!.location
        
        //squats angles decleration
        let firstAngle = atan2(rightHip.y - rightKnee.y, rightHip.x - rightKnee.x) // simple trigonometry to determine the angles required
        let secondAngle = atan2(rightAnkle.y - rightKnee.y, rightAnkle.x - rightKnee.x)
        var angleDiffRadians = firstAngle - secondAngle //calculate the difference between the angles
        
        //Jumping Jacks angles decleration
        let firstJJAngle = atan2(rightHip.y - rightShoulder.y, rightHip.x - rightShoulder.x)
        let secondJJAngle = atan2(rightElbow.y - rightShoulder.y, rightElbow.x - rightShoulder.x) //calculate angles of armpit
        var JJangleDiffRadians = firstJJAngle - secondJJAngle
        
        let firstrootAngle = atan2(rightKnee.y - root.y, rightKnee.x - root.x)
        let secondrootAngle = atan2(leftKnee.y - root.y, leftKnee.x - root.x)
        var rootangleDiffRadians = firstrootAngle + secondrootAngle
        
        //Pushups anlges decleration
        let firstPUAngle = atan2(rightShoulder.y - rightElbow.y, rightShoulder.x - rightElbow.x)
        let secondPUAngle = atan2(rightWrist.y - rightElbow.y, rightWrist.x - rightElbow.x)
        var PUAngleDiffRadians = firstPUAngle - secondPUAngle
        
        //SLDeadlifts angles decleration
        let firstDeadliftAngle = atan2(neck.y - root.y, neck.x - root.x)
        let secondDeadLiftAngle = atan2(rightKnee.y - root.y, rightKnee.x - root.x)
        var SLDangleDiffRadians = firstDeadliftAngle - secondDeadLiftAngle
        
        let firstLegAngle = atan2(leftAnkle.y - leftKnee.y, leftAnkle.x - leftKnee.x)
        let secondLegAngle = atan2(leftHip.y - leftKnee.y, leftHip.x - leftKnee.x)
        var legAngleDiffRadians = firstLegAngle - secondLegAngle
        
        //Burpees angles decleration
        let firstBurpeeAngle = atan2(rightElbow.y - rightShoulder.y, rightElbow.x - rightShoulder.x)
        let secondBurpeeAngle = atan2(neck.y - rightShoulder.y, neck.x - rightShoulder.x)
        var BurpeeAngleDiffRadians = firstBurpeeAngle - secondBurpeeAngle
        
        //conversion of angles in radians to degrees
        while angleDiffRadians < 0 {
                    angleDiffRadians += CGFloat(2 * Double.pi)
                }
        while JJangleDiffRadians < 0{
            JJangleDiffRadians += CGFloat(2 * Double.pi)
        }
        while rootangleDiffRadians < 0{
            rootangleDiffRadians += CGFloat(2 * Double.pi)
        }
        while PUAngleDiffRadians < 0{
            PUAngleDiffRadians += CGFloat(2 * Double.pi)
        }
        while SLDangleDiffRadians < 0{
            SLDangleDiffRadians += CGFloat(2 * Double.pi)
        }
        while legAngleDiffRadians < 0{
            legAngleDiffRadians += CGFloat(2 * Double.pi)
        }
        while BurpeeAngleDiffRadians < 0{
            BurpeeAngleDiffRadians += CGFloat(2 * Double.pi)
        }
        
        
        let angleDiffDegrees = Int(angleDiffRadians * 180 / .pi)

        let rootangleDIffDegrees = Int(rootangleDiffRadians * 180 / .pi)
        let JJangleDIffDegrees = Int(JJangleDiffRadians * 180 / .pi)
        
        let PUAngleDiffDegrees = Int(PUAngleDiffRadians * 180 / .pi)
        let SLDAngleDiffDegrees = Int(SLDangleDiffRadians * 180 / .pi)
        let legAngleDiffDegrees = Int(legAngleDiffRadians * 180 / .pi)
        let BurpeeAngleDiffDegrees = Int(BurpeeAngleDiffRadians * 180 / .pi)
        
        
        //process whether the player is doing a squat
        if angleDiffDegrees > 150 && self.wasInBottomPosition == true{ // determine if the player is doing a squat
            self.wasInBottomPosition = false
            self.squatCount += 1
            self.caloriesBurntSquats = Float(self.squatCount) * 0.32
        }
        
        //process whether the player is doing a lunge
        if angleDiffDegrees > 160 && wasInBottomLunge == true{
            self.LungeCounter += 1
            self.wasInBottomLunge = false
            self.caloriesBurntLunges = Float(self.LungeCounter) * 0.3
        }
        
        //process whether the player is doing a Jumping Jack
        if rootangleDIffDegrees < 180 && JJangleDIffDegrees <= 90 && self.wasInTopJJ{
            self.JumpingJackCounter += 1
            self.wasInTopJJ = false
            self.caloriesBurntJJ = Float(self.JumpingJackCounter) * 0.2
        }
        //process whether the player is doing a pushup
        if PUAngleDiffDegrees > 167 && wasInBottomPU{
            halfPU += 1
            wasInBottomPU = false
            self.caloriesBurntPushups = Float(self.PushupCounter) * 0.6
        }
        
        if halfPU >= 2{
            self.halfPU = 0
            self.PushupCounter += 1
        }
        //process whether the player is doing a SLDeadlift
        if SLDAngleDiffDegrees >= 215 && wasInDeadLift{
            SLDeadliftsCounter += 1
            wasInDeadLift = false
            self.caloriesBurntSLD = Float(self.SLDeadliftsCounter) * 0.2
        }
        //process whether the player is doing a burpee
        if self.wasinupright == true && self.wasinalmostdoneBurpee == true{
            self.halfburpee += 1
            self.wasinupright = false
            self.wasInBottomBurpee = false
            self.wasInMidBurpee = false
            self.down = false
            self.wasinalmostdoneBurpee = false
            self.caloriesBurntBurpees = Float(self.BurpeesCounter) * 0.5
        }
        if self.halfburpee >= 2{
            self.BurpeesCounter += 1
            self.halfburpee = 0
        }
        
        //proess whether the player is in the top/bottom position of the exercise
        let hipHeight = rightHip.y
        let lefthipHeight = leftHip.y
        let kneeHeight = rightKnee.y
        let leftkneeHeight = leftKnee.y
        let ankleheight = rightAnkle.y
        let shoulderheight = rightShoulder.y
        let elbowHeight = rightElbow.y
        let wristheight = leftWrist.y
        
        
        //squats
        if hipHeight < kneeHeight { //determing if the user is in the bottom position of the squat
            self.wasInBottomPosition = true
        }
        //lunges
        if leftkneeHeight <= ankleheight {
            self.wasInBottomLunge = true
        }
        if leftkneeHeight == kneeHeight {
            self.uprightLunge = true
        }else{
            self.uprightLunge = false
        }
        
        //Jumping jacks
        if elbowHeight > shoulderheight && JJangleDIffDegrees >= 130 && rootangleDIffDegrees > 175{ // determining if the user's arms are above the shoulders
            self.wasInTopJJ = true
        }
        //Pushups
        if PUAngleDiffDegrees <= 150 {
            self.wasInBottomPU = true
        }
        //SL deadlifts
        if hipHeight > lefthipHeight{
            self.wasInDeadLift = true
        }
        //Burpees
        if BurpeeAngleDiffDegrees >= 240{
            self.down = true
        }
        if kneeHeight >= elbowHeight{
            self.wasInMidBurpee = true
        }
        if kneeHeight < elbowHeight && self.wasInMidBurpee == true && self.down == true{
            self.wasInBottomBurpee = true
        }
        if kneeHeight >= elbowHeight && self.wasInBottomBurpee == true{
            self.wasinalmostdoneBurpee = true
        }
        if wristheight > ankleheight{
            self.wasinupright = true
        }else{
            self.wasinupright = false
        }
        

        let kneeDistance = rightKnee.distance(to: leftKnee)
        let ankleDistance = rightAnkle.distance(to: leftAnkle)
        let wristDistance = rightWrist.distance(to: leftWrist)
        let shoulderDistance = rightShoulder.distance(to: leftShoulder)
        
        
        if ankleDistance > kneeDistance { //checking if the posture of the player doing squats is good
            self.isGoodSquatPosture = false
        } else {
            self.isGoodSquatPosture = true
        }
        
        if rootangleDIffDegrees >= 180 && elbowHeight <= shoulderheight{ //posture check for Jumping Jacks
            self.isGoodJJPosture = false
        } else{
            self.isGoodJJPosture = true
        }
        if (wristDistance - 1) != shoulderDistance { //posture check for PushUps
            self.isGoodPUPosture = false
        }else{
            self.isGoodPUPosture = true
        }
        if legAngleDiffDegrees > 175{ //posture check for Single-Leg Deadlifts
            self.isGoodSLDPosture = false
        }else{
            self.isGoodSLDPosture = true
        }
        
    }
}

my viewController code:

import SwiftUI

class DoneViewController: UIViewController {
    
    var exerciseLoaded = false
    var burntCalories = Float()
    
    let poseEstimator = PoseEstimator()
    
    @IBOutlet weak var repsDone: UILabel!
    
    @IBOutlet weak var caloriesBurnt: UILabel!
    
    @IBOutlet weak var exerciseMoreBtn: UIButton!
    
    @IBOutlet weak var HomeWhenDone:
        UIButton!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        exerciseMoreBtn.layer.cornerRadius = 10
        HomeWhenDone.layer.cornerRadius = 10
        repsDone.text = String(poseEstimator.squatCount)
    }

Upvotes: 0

Views: 728

Answers (1)

SeaSpell
SeaSpell

Reputation: 758

Add this singleton to the PoseEstimator class

static let shared = PoseEstimator()

then in DoneViewController change

    let poseEstimator = PoseEstimator()

to:

    let poseEstimator = PoseEstimator.shared

The reason why this works is because it will be a reference to the same object. Currently you are creating a new instance by calling init from the view controller. You could also pass a reference into the view controller during navigation or retrieve it, but this is the simplest way to get you going.

Upvotes: 2

Related Questions