I am programming an iOS app using swift where i have 3 UILabels which will show data different sensors data into the same corresponding labels.
These are 3 labels which i am using.
#IBOutlet weak var xAccel: UILabel!
#IBOutlet weak var yAccel: UILabel!
#IBOutlet weak var zAccel: UILabel!
I am using UIsegmentedControl to change the display of data which is as follows.
#IBAction func AccelDidChange(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
myAccelerometer()
break
case 1:
myGyroscope()
break
default:
myAccelerometer()
}
Above used 2 functions are as follows
func myAccelerometer() {
// sets the time of each update
motion.accelerometerUpdateInterval = 0.1
//accessing the data from the accelerometer
motion.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in
// can print the data on the console for testing purpose
//print(data as Any)
if let trueData = data {
self.view.reloadInputViews()
//setting different coordiantes to respective variables
let x = trueData.acceleration.x
let y = trueData.acceleration.y
let z = trueData.acceleration.z
//setting the variable values to label on UI
self.SensorName.text = "Accelerometer Data"
self.xAccel.text = "x : \(x)"
self.yAccel.text = "y : \(y)"
self.zAccel.text = "z : \(z)"
}
}
}
func myGyroscope() {
motion.gyroUpdateInterval = 0.1
motion.startGyroUpdates(to: OperationQueue.current!) { (data, error) in
if let trueData = data {
self.view.reloadInputViews()
//setting different coordiantes to respective variables
let x = trueData.rotationRate.x
let y = trueData.rotationRate.y
let z = trueData.rotationRate.z
//setting the variable values to label on UI
self.SensorName.text = "Gyroscope Data"
self.xAccel.text = "x: \(x)"
self.yAccel.text = "y: \(y)"
self.zAccel.text = "z: \(z)"
}
}
}
**
Problem is it keeps on displaying both the Accelerometer and Gyroscope data on UILabels at the same time instead of only showing the data of a particular sensor when tapped. I have tried to use the break option but still not working. If any one could point out the possible solution, that would be great. Thanks
**
EIDT -
Here is the output on screen where you can see the values fluctuates between different sensors. I only want readings from 1 sensor at a time.
https://imgur.com/a/21xW4au
#IBAction func AccelDidChange(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
motion.stopGyroUpdates()
myAccelerometer()
break
case 1:
motion.stopDeviceMotionUpdates()
myGyroscope()
break
default:
myAccelerometer()
}
}
You need to stop unneeded resource before the switch. Try this please
Related
I've followed a few things including Apple's documentation on the core motion. This is probably very simple to do, I just can't put my finger on it. Basically, I'm trying to get all of the X, Y, and Z data, combine it and make it constantly add to the "scoreValue" text. Basically, a constant counter that counts the total degrees rotated ever.
import UIKit
import CoreMotion
class ViewController: UIViewController {
#IBOutlet var scoreValue: UITextView!
#IBOutlet weak var Label: UILabel!
var motion = CMMotionManager()
override func viewDidLoad() {
super.viewDidLoad()
myGyroscope()
view.backgroundColor = .systemBlue
}
func myGyroscope() {
motion.gyroUpdateInterval = 0.1
motion.startGyroUpdates(to: OperationQueue.current!) { [self]
(data, error) in
print(data as Any)
if let trueData = data {
self.view.reloadInputViews()
let x = trueData.rotationRate.x
let y = trueData.rotationRate.y
let z = trueData.rotationRate.z
self.UNDEFINED.text = "\(Double(x).rounded(toPlaces :0))"
self.UNDEFINED.text = "\(Double(y).rounded(toPlaces :0))"
self.UNDEFINED.text = "\(Double(z).rounded(toPlaces :0))"
}
}
return
}
}
extension Double {
/// Rounds the double to decimal places value
func rounded(toPlaces places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return (self * divisor).rounded() / divisor
}
}
I have the below function that works properly when a button is switched to activate it. I want to add a variable that gives each message it's current message number starting from 1. I've tried different methods of setting / updating the value but none of it helped with updating.
I'm very new to Swift / iOS development so I'm sure there is something I'm missing. What I do know is that the message prints to console repeatedly till the button is switched off and the Timer is what enables it to continuously run.
#IBOutlet weak var stateLabel: UILabel!
//Starts / Stops recording of sensor data via a switch
#IBAction func stateChange(_ sender: UISwitch) {
if sender.isOn == true {
startSensorData()
stateLabel.text = "Stop"
} else {
stopSensorData()
stateLabel.text = "Start"
}
}
func startSensorData() {
print("Start Capturing Sensor Data")
// Making sure sensors are available
if self.motionManager.isAccelerometerAvailable, self.motionManager.isGyroAvailable {
// Setting the frequency required for data session
self.motionManager.accelerometerUpdateInterval = 1.0 / 3.0
self.motionManager.gyroUpdateInterval = 1.0 / 3.0
// Start sensor updates
self.motionManager.startAccelerometerUpdates()
self.motionManager.startGyroUpdates()
// Configure a timer to fetch the data.
self.motionUpdateTimer = Timer.scheduledTimer(withTimeInterval: 1.0/3.0, repeats: true, block: { (timer1) in
// Get the motion data.
var loggingSample = 1
if let accelData = self.motionManager.accelerometerData, let gyroData = self.motionManager.gyroData {
let accelX = accelData.acceleration.x
let accelY = accelData.acceleration.y
let accelZ = accelData.acceleration.z
let gyroX = gyroData.rotationRate.x
let gyroY = gyroData.rotationRate.y
let gyroZ = gyroData.rotationRate.z
let message = "\(Date().timeIntervalSince1970),\(self.device_id),\(loggingSample),\(accelX),\(accelY),\(accelZ),\(gyroX),\(gyroY),\(gyroZ),Processing"
print(message)
loggingSample += 1
}
}
)}
}
You keep getting a value of 1 for loggingSample because you are using a local variable that gets created as 1 each time.
All you need to do is move the declaration of loggingSample to be outside the function so it is a class property.
Move the line:
var loggingSample = 1
outside the function so it is next to your outlets and other properties.
I need help to use accelerometer with swift 3.
This is my code:
var motion = CMMotionManager()
#IBOutlet weak var statusAccel: UILabel!
override func viewDidAppear(_ animated: Bool) {
motion.startAccelerometerUpdates(to: OperationQueue.current!){
(data , error) in
if let trueData = data {
self.view.reloadInputViews()
self.statusAccel.text = "\(trueData)"
}
}
}
It works but it just show me X Y and Z and i want to use Z.
Example : if Z = 2 do something
You can access the acceleration on the Z-axis by calling CMAccelerometerData.acceleration.z. If you are unsure about how to access a certain property of a class, always check the documentation either in Xcode directly or on Apple's documentation website, you can save a lot of time with this approach.
motion.startAccelerometerUpdates(to: OperationQueue.current!, withHandler: { data, error in
guard error == nil else { return }
guard let accelerometerData = data else { return }
if accelerometerData.acceleration.z == 2.0 {
//do something
}
})
The data object that gets returned by startAccelerometerUpdates(...) is of type CMAccelerometerData which has a CMAcceleration property. From this you can get the z component.
var motion = CMMotionManager()
#IBOutlet weak var statusAccel: UILabel!
override func viewDidAppear(_ animated: Bool) {
motion.startAccelerometerUpdates(to: OperationQueue.current!){
(data , error) in
if let trueData = data {
self.view.reloadInputViews()
self.statusAccel.text = "\(trueData)"
if trueData.acceleration.z == 2 {
// do things...
}
}
}
}
I´m trying to display the cadence and pace data of CMPedometer. When I run the application with my phone attached, it writes the output of the data immediately through the print("...") function into the console, but takes multiple turns until it displays the data in the UILabel.
How can I get the data as fast as possible so I can use them?
Best, Zack
import UIKit
import CoreMotion
class ViewController: UIViewController {
let pedometer = CMPedometer()
#IBOutlet weak var paceLabel: UILabel!
#IBOutlet weak var cadenceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
guard CMPedometer.isCadenceAvailable() && CMPedometer.isPaceAvailable() else{
print("Pace and cadence data are not available")
return
}
let oneWeekAgo = NSDate(timeIntervalSinceNow: -(7 * 24 * 60 * 60))
pedometer.startUpdates(from: oneWeekAgo as Date) {data, error in
guard let pData = data , error == nil else{
return
}
//The current pace of the user, measured in seconds per meter. (1 step = 83cm?)
if let pace = pData.currentPace{
print("Pace = \(pace)")
self.paceLabel.text = "Pace = \(round(Double(pace))*10/10)"
}
//The rate at which steps are taken, measured in steps per second.
if let cadence = pData.currentCadence{
self.cadenceLabel.text = "Cadence = \(cadence))"
print("Cadence = \(cadence)")
}
}// -----------------oneWeekAgo
}// -----------------ViewDidLoad
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}//-------------------- UIViewController
The update block is called on a background thread and you need to update your UI on the main thread. Wrap the UI update calls in a dispatch back to the main thread:
Dispatch.main.async {
//The current pace of the user, measured in seconds per meter. (1 step = 83cm?)
if let pace = pData.currentPace{
print("Pace = \(pace)")
self.paceLabel.text = "Pace = \(round(Double(pace))*10/10)"
}
//The rate at which steps are taken, measured in steps per second.
if let cadence = pData.currentCadence{
self.cadenceLabel.text = "Cadence = \(cadence))"
print("Cadence = \(cadence)")
}
}
}
I recently wanted to code an Average-Calculator.
My plan was to build a UITextField in which you can type Numbers separated by commas... By pressing the 'Calculate' button the App should calculate the Average of the Numbers above and give them out by setting a labeltext to the average.
So I wrote my average function and received this error message:
Can not convert value of type 'UITextField' to expected element type 'Double'.
This is my Code:
#IBOutlet var Input: UITextField!
#IBOutlet var Output: UILabel!
#IBAction func Calculate(sender: AnyObject) {
var grades:[Double] = [Input]
func average(nums: [Double]) -> Double {
var total = 0.0
for grade in nums{
total += Double(grade)
}
let gradesTotal = Double(nums.count)
let average = total/gradesTotal
return average
}
let Average = average(grades)
Output.text = "Average: \(Average)"
}
Can you help me with my idea?
Is there a better way to get an input?
You need to separate numbers to get that array if you do that:
you can pass "doubles" array to your average function
code to copypaste :)
var str = "1,2,3,4,5,6"
let stringsWithNumbers = str.componentsSeparatedByString(",")
let doubles = stringsWithNumbers.map { Double($0)! }
Please use lower camel case for variables...
In this line:
var grades:[Double] = [Input]
Input is an instance of UITextField, so you are trying to assign a single-element Array<UITextField> to Array<Double>. You see you cannot do such sort of things.
If you want to accept a text which contains Numbers separated by commas, you need to explicitly convert the text to [Double].
To simplify, let's just ignore the nil or non-numeric values.
Then you need to change your code as:
#IBOutlet var input: UITextField!
#IBOutlet var output: UILabel!
#IBAction func calculate(sender: AnyObject) {
var grades: [Double] = (input.text ?? "").componentsSeparatedByString(",").flatMap{Double($0)}
func average(nums: [Double]) -> Double {
var total = 0.0
for grade in nums{
total += Double(grade)
}
let gradesTotal = Double(nums.count)
let average = total/gradesTotal
return average
}
let averageValue = average(grades)
output.text = "Average: \(averageValue)"
}
The basic idea of this line:
var grades: [Double] = (input.text ?? "").componentsSeparatedByString(",").flatMap{Double($0)}
is well-described in Lu_'s answer. Mine is just a little safer version.
(Addition)
Some explanation about safety:
UITextFields property text is of type String?, so you should think it can be nil. Giving a default value for nil with ?? operator.
And using Double($0)! may crash your app, as Double($0) will return nil for non-numeric strings.
Writing these reminded me one more crash case.
When gradesTotal == 0, the code above will crash with division by zero.
(The default value does not work well for "safety" in the code above...)
So, one more step ahead to safety:
#IBAction func calculate(sender: AnyObject) {
var grades: [Double] = (input.text ?? "").componentsSeparatedByString(",").flatMap{
Double($0.stringByTrimmingCharactersInSet(.whitespaceCharacterSet()))
}
func average(nums: [Double]) -> Double? {
var total = 0.0
for grade in nums{
total += Double(grade)
}
let gradesTotal = Double(nums.count)
if gradesTotal > 0 {
let average = total/gradesTotal
return average
} else {
return nil
}
}
if let averageValue = average(grades) {
output.text = "Average: \(averageValue)"
} else {
output.text = "Average not available"
}
}
What you have to do is using Double(Input.text) instead of [Input]. Right now, you were trying to convert a UITextField to a double, which causes the error.
let textInput = txtInputView.text;
let components = textInput.componentsSeparatedByString(",");
let sum = 0.0;
for txt in components
{
sum = sum + Double(txt);
}
let avg = sum / components.count;
print(avg)
Here is the complete code if someone is interested :)
#IBOutlet var input: UITextField!
#IBOutlet var output: UILabel!
#IBAction func calculate(sender: AnyObject) {
var grades: [Double] = (input.text ?? "").componentsSeparatedByString(",").flatMap{
Double($0.stringByTrimmingCharactersInSet(.whitespaceCharacterSet()))
}
func average(nums: [Double]) -> Double? {
var total = 0.0
for grade in nums{
total += Double(grade)
}
let gradesTotal = Double(nums.count)
if gradesTotal > 0 {
let average = total/gradesTotal
return average
} else {
return nil
}
}
if let averageValue = average(grades) {
output.text = "Average: \(averageValue)"
} else {
output.text = "Average not available"
}
}
#IBAction func userTappedCalculate(sender: AnyObject) { view.endEditing(true)
}
I added the line #IBAction func userTappedCalculate(sender: AnyObject) { view.endEditing(true)
} to close the input TextField when you tap calculate...