iosswiftuisegmentedcontrolsegmentedcontrol

How to dinamically change values in labels in a SegmentedControl? Swift 4, iOS


I have some issues with the implementation of a segmented control. I come to you (the more experienced people) to get some advice about what I should do in order to fix this. Here I go:

For "current" tab, the "Panel's optimal azimuth" and "Panel's optimal tilt angle" should be "tracking the sun" (a code that I will add later), in other words: those 2 fields must update in real time (again, that code I will do it later). The "Panel's current azimuth" and "Panels current tilt angle" basically change according to the phone's position (I use coreMotion and coreLocation to get them).

The problem is this (images added after the explanation):

For the "current" tab, the fields of "panel's optimal azimuth" and "Panel's optimal tilt angle" must change with time. "Panel's current tilt angle" must show from the beginning the panel's (phone's) tilting and it doesn't do it (only does it when you select another tab).

The "Panel's Optimal Tilt Angle" must have a fixed (but different) value for the 3, 6 months and "Year" tabs. "Panel's Optimal Azimuth" is the same for the 3, 6 months and "year" tabs (that code I will add it later as well). Nevertheless, I have problems when I select the "Current" tab, here, the "Panel's optimal Azimuth" gets the value "tracking the sun", and when I choose another tab, that value remains when it should say "180º". This only happens when the phone is still on a surface, as soon I move the phone, the value changes to 180º in this field. The opposite happens when I go from other tabs to the "current" tab (instead of showing "tracking the sun" it shows "180º", again, this value appears when the phone is moving, but if the phone is still, "tracking the sun" is shown).

Basically the only field that's properly working is the "Panel's current Azimuth" in all the 4 tabs in the segmented control.

Please refer to the images to get a better idea.

What I get in the initial view

What I want in the initial view

What I get if the phone is moving (this is correct for the 3, 6 months and year tabs)

What I get if the phone is still, if I chose the "current" tab while the phone was still and then I selected another tab

Same as previos image

So, the question that I have is: What can I do in order to prevent this? I tried to use a switch-case statement inside an @IBAction corresponding to the segmented control, but it is evident that it doesn't work. Here I share with you the code that I have used for my project:

import UIKit
import CoreMotion
import CoreLocation

class PVOrientationViewController: UIViewController, CLLocationManagerDelegate {

//MARK: - Constants and Variables
let motionManager = CMMotionManager()
let locationManager = CLLocationManager()
var segIndex = 0

//MARK: - Outlets, views, actions
@IBOutlet weak var panelOptimalAz: UILabel!
@IBOutlet weak var panelOptimalTilt: UILabel!
@IBOutlet weak var panelCurrentAz: UILabel!
@IBOutlet weak var panelCurrentTilt: UILabel!

@IBOutlet weak var segmentedControl: UISegmentedControl!

//MARK: - Action of the Segmented Button.
@IBAction func indexChanged(_ sender: Any) {

    segIndex = segmentedControl.selectedSegmentIndex

    switch segIndex {

    case 0:
        print("Current Tab")
        panelOptimalAz.text = "Tracking the Sun"
        panelCurrentTilt.text = myDeviceMotion()
    case 1:
        print("3 months Tab")
        panelCurrentTilt.text = String(myDeviceMotion())

    case 2:
        print("6 months Tab")
        panelCurrentTilt.text = String(myDeviceMotion())

    case 3:
        print("Year tab")
        panelCurrentTilt.text = String(myDeviceMotion())

    default:
        panelOptimalAz.text = "No Optimal Azimuth given"
        panelCurrentAz.text = "No Current Azimuth given"
        panelCurrentTilt.text = "No Current Tilt given"
        panelOptimalTilt.text = "No Optimal Tilt given"

    }

}

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

    locationManager.delegate = self

    // Azimuth
    if (CLLocationManager.headingAvailable()) {

        locationManager.headingFilter = 1
        locationManager.startUpdatingHeading()

    }

}

//MARK: - Motion methods
func myDeviceMotion() -> String{

    var currentTilt:String = "0.0º"
    if motionManager.isDeviceMotionAvailable {

        motionManager.deviceMotionUpdateInterval = 0.1

        motionManager.startDeviceMotionUpdates(to: OperationQueue()) {(motion, error) -> Void in

            if let attitude = motion?.attitude {

                DispatchQueue.main.async{

                    self.panelCurrentTilt.text = "\(String(format:"%.0f", attitude.pitch * 180 / Double.pi))º"

                    currentTilt = "\(String(format:"%.0f", attitude.pitch * 180 / Double.pi))º"

                }

            }

        }

        print("Device motion started")

    }else {

        print("Device motion unavailable")

    }

    return currentTilt

}

//MARK: - True heading and panel's heading
func locationManager(_ manager: CLLocationManager, didUpdateHeading heading: CLHeading) {

    let trueHeading = heading.trueHeading

    self.panelCurrentAz.text = "\(String(format: "%.0f", trueHeading))º"

    //If latitude >= 0, then panel's azimuth = 180º, else, it is 0º
    var panelHeading = 0.0
    if GlobalData.latit >= 0.0{

        self.panelOptimalAz.text = "180º"
        panelHeading = trueHeading - 180.0

        if panelHeading < 0{

            panelHeading += 360.0
            self.panelCurrentAz.text = "\(String(format: "%.0f", panelHeading))º"

        } else {

            panelHeading += 0.0
            self.panelCurrentAz.text = "\(String(format: "%.0f", panelHeading))º"

        }

    }else{

        self.panelOptimalAz.text = "0º"
        panelHeading = trueHeading + 180.0

        if panelHeading > 359.0{

            panelHeading -= 360.0
            self.panelCurrentAz.text = "\(String(format: "%.0f", panelHeading))º"

        } else {

            panelHeading += 0.0
            self.panelCurrentAz.text = "\(String(format: "%.0f", panelHeading))º"

        }

    }

}

}

Thanks in advance for your time and your advice.

Greetings.


Solution

  • Recapping...

    OptimalAz label should say "Tracking the Sun" if the current selected segment is "Current", so add an if () in didUpdateHeading to only update that label when the other segments are selected.

    Start the motion manager in viewDidLoad() (along with location manager)... it will continue to run - and continue to update the labels - so no need for a myDeviceMotion() func.

    At the end of viewDidLoad(), trigger indexChanged() to initialize the labels (and any other needed tasks / vars) as if the user tapped the "Current" segment.

    Give this a try (I think I put enough comments in the code to explain any changes):

    import UIKit
    import CoreMotion
    import CoreLocation
    
    class PVOrientationViewController: UIViewController, CLLocationManagerDelegate {
    
        //MARK: - Constants and Variables
        let motionManager = CMMotionManager()
        let locationManager = CLLocationManager()
        var segIndex = 0
    
        //MARK: - Outlets, views, actions
        @IBOutlet weak var panelOptimalAz: UILabel!
        @IBOutlet weak var panelOptimalTilt: UILabel!
        @IBOutlet weak var panelCurrentAz: UILabel!
        @IBOutlet weak var panelCurrentTilt: UILabel!
    
        @IBOutlet weak var segmentedControl: UISegmentedControl!
    
        //MARK: - Action of the Segmented Button.
        @IBAction func indexChanged(_ sender: Any) {
    
            segIndex = segmentedControl.selectedSegmentIndex
    
            switch segIndex {
    
            case 0:
                print("Current Tab")
                panelOptimalAz.text = "Tracking the Sun"
    
            case 1:
                print("3 months Tab")
                // do something related to 3 months
    
            case 2:
                print("6 months Tab")
                // do something related to 6 months
    
            case 3:
                print("Year tab")
                // do something related to Year
    
            default:
                panelOptimalAz.text = "No Optimal Azimuth given"
                panelCurrentAz.text = "No Current Azimuth given"
                panelCurrentTilt.text = "No Current Tilt given"
                panelOptimalTilt.text = "No Optimal Tilt given"
    
            }
    
        }
    
        //MARK: - viewDidLoad()
        override func viewDidLoad() {
            super.viewDidLoad()
    
            locationManager.delegate = self
    
            // Azimuth
            if (CLLocationManager.headingAvailable()) {
    
                locationManager.headingFilter = 1
                locationManager.startUpdatingHeading()
    
            }
    
            // start the motion manager here, since we want it running all the time
            if motionManager.isDeviceMotionAvailable {
    
                motionManager.deviceMotionUpdateInterval = 0.1
    
                motionManager.startDeviceMotionUpdates(to: OperationQueue()) {(motion, error) -> Void in
    
                    if let attitude = motion?.attitude {
    
                        DispatchQueue.main.async{
    
                            self.panelCurrentTilt.text = "\(String(format:"%.0f", attitude.pitch * 180 / Double.pi))º"
    
                        }
    
                    }
    
                }
    
                print("Device motion started")
    
            }else {
    
                print("Device motion unavailable")
    
            }
    
            // call segment changed to update on start (as if user tapped seg 0)
            self.indexChanged(segmentedControl)
    
        }
    
        //MARK: - Motion methods
    
        // no need for this
    //  func myDeviceMotion() -> String{
    //
    //      var currentTilt:String = "0.0º"
    //
    //      return currentTilt
    //
    //  }
    
        //MARK: - True heading and panel's heading
        func locationManager(_ manager: CLLocationManager, didUpdateHeading heading: CLHeading) {
    
            let trueHeading = heading.trueHeading
    
            //If latitude >= 0, then panel's azimuth = 180º, else, it is 0º
    
            var panelHeading = 0.0
    
            // I don't have your GlobalData object, so
            //      just hard-coding 1.0 here...
    
    //      if GlobalData.latit >= 0.0{
            if 1.0 >= 0.0 {
    
                // only update Optimal Az label if "Current" seg is NOT selected
                if self.segmentedControl.selectedSegmentIndex != 0 {
                    self.panelOptimalAz.text = "180º"
                }
    
                panelHeading = trueHeading - 180.0
    
                if panelHeading < 0{
    
                    panelHeading += 360.0
    
                }
    
                self.panelCurrentAz.text = "\(String(format: "%.0f", panelHeading))º"
    
            }else{
    
                // only update Optimal Az label if "Current" seg is NOT selected
                if self.segmentedControl.selectedSegmentIndex != 0 {
                    self.panelOptimalAz.text = "0º"
                }
    
                panelHeading = trueHeading + 180.0
    
                if panelHeading > 359.0{
    
                    panelHeading -= 360.0
    
                }
    
                self.panelCurrentAz.text = "\(String(format: "%.0f", panelHeading))º"
    
            }
    
        }
    
    }