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)
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.
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))º"
}
}
}