#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
inDoneViewController
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
личный, если вы собираетесь использовать одноэлементное решение, чтобы случайно не создать несколько экземпляров PoseEstimator3. но как мне изменить свой код на инициализированный приватный
4. @KiranLim В swift вы используете слово private перед объявлением инициализации. Если вы хотите узнать больше об управлении доступом, посмотрите здесь docs.swift.org/swift-book/LanguageGuide/AccessControl.html