Доступ к переменной из другого класса дает 0 быстрых

#swift

Вопрос:

Поэтому я хочу получить доступ к этой переменной squatCount из другого вызываемого класса PoseEstimator и распечатать ее в UILabel в другом ViewController.Но по какой-то причине, когда я это делаю, метка выводит значение squatCount как ноль, хотя переменная явно увеличилась.

Я читал в Интернете и часами гуглил форумы, на которых я читал, рассказывая о реализации какой-то глобальной переменной или синглтона или какой-то перечислительной штуки, я действительно не понимаю, что это совсем новое для swift. Поэтому я пытаюсь выяснить, почему это происходит, и решить эту проблему. Мы очень признательны за помощь.

Код оценщика положения:

 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: amp;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 amp;amp; 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 amp;amp; 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 amp;amp; JJangleDIffDegrees <= 90 amp;amp; 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 amp;amp; 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 amp;amp; wasInDeadLift{
            SLDeadliftsCounter  = 1
            wasInDeadLift = false
            self.caloriesBurntSLD = Float(self.SLDeadliftsCounter) * 0.2
        }
        //process whether the player is doing a burpee
        if self.wasinupright == true amp;amp; 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 amp;amp; JJangleDIffDegrees >= 130 amp;amp; 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 amp;amp; self.wasInMidBurpee == true amp;amp; self.down == true{
            self.wasInBottomBurpee = true
        }
        if kneeHeight >= elbowHeight amp;amp; 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 amp;amp; 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
        }
        
    }
}
 

мой код контроллера просмотра:

 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)
    }
 

Комментарии:

1. вы используете poseEstimator.squatCount in DoneViewController viewDidLoad сразу после инициализации PoseEstimator , но перед установкой значения squatCount и squatCount имеет значение по умолчанию 0, поэтому вы получаете значение по умолчанию.

2. Существует множество возможных решений, но без знания контекста трудно понять, какое из них правильное, как вы используете класс PoseEstimator и является ли DoneViewController, поскольку название подразумевает то, к чему вы обращаетесь после того, как закончите использовать класс. Я также не понимаю, почему вы смешиваете здесь UIKit и SwiftUI.

Ответ №1:

Добавьте этот синглтон в класс PoseEstimator

 static let shared = PoseEstimator()
 

затем в DoneViewController измените

     let poseEstimator = PoseEstimator()
 

Для:

     let poseEstimator = PoseEstimator.shared
 

Причина, по которой это работает, заключается в том, что это будет ссылка на один и тот же объект. В настоящее время вы создаете новый экземпляр, вызывая инициализацию из контроллера представления. Вы также можете передать ссылку в контроллер представления во время навигации или получить ее, но это самый простой способ начать работу.

Комментарии:

1. подождите, это все, что мне нужно было, это изменить self.squatCount = 1 на PoseEstimator.shared.squatCount = 1, и это сделало свое дело

2. @KiranLim Вы также должны сделать свой init личный, если вы собираетесь использовать одноэлементное решение, чтобы случайно не создать несколько экземпляров PoseEstimator

3. но как мне изменить свой код на инициализированный приватный

4. @KiranLim В swift вы используете слово private перед объявлением инициализации. Если вы хотите узнать больше об управлении доступом, посмотрите здесь docs.swift.org/swift-book/LanguageGuide/AccessControl.html