I don't know how to use SwiftUI and I need to put in a label = 0 and for every time your iphone y > 3 its adds 1 to the label.
motionManager.gyroUpdateInterval = 0.2
motionManager.startGyroUpdates(to: OperationQueue.current!) { (data, Error) in
if let myData = data
{
if myData.rotationRate.y > 3
{
tiltOut = tiltOut + 1
self.tiltOutput.text = "\(tiltOut)"
}
I want to view the 0 on the screen and have it add 1 for every y > 3.
You need a #State var labelText: String for this.
When you update that text the label should update automatically.
struct MotionView: View {
#State private var labelText: String = ""
var body: some View {
Text(self.labelText)
.onAppear {
self.startGyro()
}
}
func startGyro() {
motionManager.gyroUpdateInterval = 0.2
motionManager.startGyroUpdates(to: OperationQueue.current!) { (data, Error) in
if let myData = data
{
if myData.rotationRate.y > 3
{
tiltOut = tiltOut + 1
self.tiltOutput.text = "\(tiltOut)"
}
}
}
}
This should work. But I didn't test it.
Related
There is 2 variable. Variable day1 is bind to textfield, the variable 2 should has been a results of an if statement. the objective is to singularise a double digit no. e.g if the number is 16, the return will be 1+6 = 7, while 29 will gv me 2 + 9 = 11, then 1 + 1 = 2.. ultimately, every numbers will be singularise, and the computation should stop when the adding reach a single numbers.
I'm a beginner.
#State private var day1 = ""
#State private var dday: Int??
var day2: String {
let day4 = day1.prefix(1)
return String(day4) }
var day3: String {
let day5 = day1.suffix(1)
return String(day5) }
var dayCount: Int {
let dd1 = Int(day2) ?? 0
let dd2 = Int(day3) ?? 0
let dd3 = dd1 + dd2
return dd3 }
var dC0: String {
let dc = String(dayCount)
return dc }
var dayCount1: Int {
let dc1 = dC0.prefix(1)
let dc2 = dC0.suffix(1)
let dc3 = Int(dc1) ?? 0
let dc4 = Int(dc2) ?? 0
let dc5 = dc3 + dc4
return dc5 }
var body: some View {
VStack {
TextField("day", text: $day1).keyboardType(.numberPad).frame(width: 70, height: 20).padding().background(Color.gray).cornerRadius(15).padding().font(.largeTitle)
Text("\(dayCount1)").font(.title).fontWeight(.heavy).padding(3).frame(width: 70, height: 20).padding().background(Color.gray).cornerRadius(15).padding()
if dayCount > 9 { Text("\(dayCount1)")
.font(.largeTitle)
.foregroundColor(Color.red)} else {Text("\(dayCount)")
.font(.largeTitle)
.foregroundColor(Color.red)}
}
}
}
I'm not sure what's your code doing. From you answer it seems like you need to get sum of digits of the number, and if this sum has more than one digit - repeat that operation, until only one digit left. You can do it like this:
struct ContentView: View {
#State private var day1 = ""
private var sum: String {
var string = day1
// repeat while number of digits is greater than 1
while string.count > 1 {
let sum = string
// convert string to list of Int digits
.compactMap { $0.wholeNumberValue }
// sum them
.reduce(0, +)
// convert sum back to string
string = String(sum)
}
return string
}
var body: some View {
VStack {
TextField("day", text: $day1).keyboardType(.numberPad).frame(width: 70, height: 20).padding().background(Color.gray).cornerRadius(15).padding().font(.largeTitle)
Text("\(sum)").font(.title).fontWeight(.heavy).padding(3).frame(width: 70, height: 20).padding().background(Color.gray).cornerRadius(15).padding()
}
}
}
I want to develop view that's loading data from Health kit (mindfulness time) so I used Timer every 1 minute to get a new data from Health kit, created by Apple watch but onReceive(Timer) are not refreshing a new data (it pass previous data only)
if I Open another app and come back to this app then it's show me a new data
import SwiftUI
struct LoadingView: View {
var healthStore : HealthStore? = HealthStore()
#State private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
#State var num : Int = 0
#Binding var showModal: Bool
var decription : String
// MARK: - BODY
var body: some View {
VStack (alignment: .center){
Spacer()
WatchView()
Spacer()
Text(decription)
.font(.callout)
.fontWeight(.semibold)
.padding(.horizontal,30)
Spacer()
}
.navigationBarBackButtonHidden(true)
.onReceive(timer) { _ in
if let healthStore = healthStore {
healthStore.requestAuthorization { success in
if success {
healthStore.getDailyMindfulnessTime { time in
print("\(time)")
}
} //: SUCCESS
}
}
}// ON RECEIVE
.onDisappear(perform: {
self.timer.upstream.connect().cancel()
})//ON DISAPPEAR
}
}
Health Store
import Foundation
import HealthKit
class HealthStore {
var healthStore : HKHealthStore?
var query : HKStatisticsCollectionQuery?
var querySampleQuery : HKSampleQuery?
init(){
// to check data is avaliable or not?
if HKHealthStore.isHealthDataAvailable(){
//Create instance of HKHealthStore
healthStore = HKHealthStore()
}
}
// Authorization
func requestAuthorization(compleion: #escaping(Bool)-> Void){
let stepType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!
let mindfulSampleType = HKSampleType.categoryType(forIdentifier: .mindfulSession)!
guard let healthStore = self.healthStore else { return compleion(false)}
healthStore.requestAuthorization(toShare: [], read: [stepType,mindfulSampleType]) { (success, error) in
compleion(success)
}
}
//Calculate steps count
func calculateSteps(completion : #escaping(HKStatisticsCollection?)->Void){
let stepType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!
let startDate = Calendar.current.date(byAdding: .day,value: -7, to: Date())
let anchorDate = Date.mondayAt12AM()
let daily = DateComponents(day:1)
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date()
, options: .strictStartDate)
//cumulativeSum (Watch+Iphone)
query = HKStatisticsCollectionQuery(quantityType: stepType, quantitySamplePredicate: predicate, options: .cumulativeSum, anchorDate: anchorDate, intervalComponents: daily)
query!.initialResultsHandler = { query, statisticsCollection , error in
completion(statisticsCollection)
}
if let healthStore = self.healthStore, let query = self.query {
healthStore.execute(query)
}
}
// DailyMindfulnessTime
func getDailyMindfulnessTime(completion: #escaping (TimeInterval) -> Void) {
let sampleType = HKSampleType.categoryType(forIdentifier: .mindfulSession)!
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
let startDate = Calendar.current.startOfDay(for: Date())
let endDate = Calendar.current.date(byAdding: .day, value: 1, to: startDate)
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
querySampleQuery = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { (_, results, error) in
if error != nil {
print(" HealthKit returned error while trying to query today's mindful sessions. The error was: \(String(describing: error?.localizedDescription))")
}
if let results = results {
var totalTime = TimeInterval()
for result in results {
totalTime += result.endDate.timeIntervalSince(result.startDate)
}
completion(totalTime)
} else {
completion(0)
}
}
if let healthStore = self.healthStore, let querySampleQuery = self.querySampleQuery {
healthStore.execute(querySampleQuery)
}
}
}
extension Date {
static func mondayAt12AM() -> Date{
return Calendar(identifier: .iso8601).date(from: Calendar(identifier: .iso8601).dateComponents([.yearForWeekOfYear,.weekOfYear],from: Date()))!
}
}
first of all you write in your question that you want to update every minute, but currently you update every second. TimeInterval is a typealias for Double and you pass it in your Timer as seconds. So in your case it should be:
#State private var timer = Timer.publish(every: 60, on: .main, in: .common).autoconnect()
Be aware this means onReceive is called after 60 seconds and not immediately.
And I tested your code and it worked fine for me. Unfortunately you didnt include your watch view so I dont know what you are doing in there.
I assumed num is the variable you wanted to update, so you need to call:
num += Int(time)
in your closure for getDailyMindfulnessTime.
If you want to display the time in your WatchView make sure to pass num as a Binding in there.
What I am trying to do is send the value of a variable from a view to a class, but I keep getting an error. Not sure what to do.
Here is the class:
class perfCalcDep: ObservableObject {
var tom:Double
var arm:Double
#Published var tempDep:String = ""
#Published var elevDep:String = ""
#Published var qnhDep:String = ""
#Published var windDep:String = ""
#Published var slopeDep:String = ""
#Published var rwyCondDep = 0
var altDep: Double {
let pressCalc = (1013 - (Double(qnhDep) ?? 1013)) * 30
return (Double(elevDep) ?? 0) + pressCalc
}
var altVar : Double { 0.21 * altDep }
var tempVar : Double { 24 * (Double(tempDep) ?? 0) }
var windVar : Double { 20.67 * (Double(windDep) ?? 0) }
var tomVar : Double { 2.22 * Double(2550-Double(tom)) }
var slpVar : Double { (Double(slopeDep) ?? 0) / 2 }
var tod : Double { (1700 + altVar + tempVar - tomVar - windVar) }
var todr : Double {
if rwyCondDep == 1 {
return (tod + ((0.1 * tod) * slpVar)) * 1.2
} else if rwyCondDep == 2 {
return (tod + ((0.1 * tod) * slpVar)) * 1.3
} else {
return (tod + ((0.1 * tod) * slpVar))
}
}
init(tom:Double, arm:Double) {
self.tom = tom
self.arm = arm
}
}
And here is part of the view:
struct TakeOffPerf: View {
// The variables I want to send to the class - their values are received from the previous view.
var tMss:Double
var tArm:Double
#ObservedObject var performance = perfCalcDep(tom: tMss, arm: tArm) // Error: Cannot use instance member 'tArm' within property initializer; property initializers run before 'self' is available
#ObservedObject var settings = Settings()
var body: some View {,,,} // just a list that shows the values from the class
Any help would be greatly appreciated.
Properties are initialised before self, so you cannot make initializing dependency between properties, but you can do this in init, eg.
struct TakeOffPerf: View {
var tMss:Double
var tArm:Double
#ObservedObject var performance: perfCalcDep // << only declare !!
#ObservedObject var settings = Settings()
init(tMss:Double, tArm:Double) {
self.tArm = tArm
self.tMss = tMss
self.performance = perfCalcDep(tom: tMss, arm: tArm)
}
// ... other code
}
Note: preserved original style, but it is good practice to name types capitalised, like PerfCalcDep
I've stumbled across this piece of code:
class TextLimiter: ObservableObject {
private let limit: Int
init(limit: Int) {
self.limit = limit
}
#Published var value = "" {
didSet {
if value.count > self.limit {
value = String(value.prefix(self.limit))
self.hasReachedLimit = true
} else {
self.hasReachedLimit = false
}
}
}
#Published var hasReachedLimit = false }
struct Strix: View {
#ObservedObject var input = TextLimiter(limit: 5)
var body: some View {
TextField("Text Input",
text: $input.value)
.border(Color.red,
width: $input.hasReachedLimit.wrappedValue ? 1 : 0 )
} }
It's a TextField limiting code where after a user inputs characters after a limit, it won't keep inputing characters inside the box. I've tried this code and after the limit is reached, it just keeps on inputting characters.
For example:
How it's supposed to work: limit is 5 so the only input allowed is 'aaaaa'
How it's behaving: limit is 5 but input allowed is 'aaaaaaaa.....'
I'm aware of a recent solution to this:
How to set textfield character limit SwiftUI?
but the solution is specifically tailored for iOS 14. I was hoping to be able to support iOS 13. Thanks.
Link to original code:
https://github.com/programmingwithswift/SwiftUITextFieldLimit/blob/master/SwiftUITextFieldLimit/SwiftUITextFieldLimit/ContentView.swift
Your solution is lies in SwiftUI's subscriber .onReceive,
Make sure that your property hasReachedLimit must not marked with #Published else it will trigger infinite loop of view body rendering.
Below shown code works as your expectation.
class TextLimiter: ObservableObject {
let limit: Int
#Published var value = ""
var hasReachedLimit = false
init(limit: Int) {
self.limit = limit
}
}
struct Strix: View {
#ObservedObject var input = TextLimiter(limit: 5)
var body: some View {
TextField("Text Input",
text: $input.value)
.border(Color.red,
width: $input.hasReachedLimit.wrappedValue ? 1 : 0 )
.onReceive(Just(self.input.value)) { inputValue in
self.input.hasReachedLimit = inputValue.count > self.input.limit
if inputValue.count > self.input.limit {
self.input.value.removeLast()
}
}
}
}
BTW this is not an efficient solution.
I have the following classes
class ListItem: Identifiable {
var id: UUID
var name: String
var description: String
var isFavorite: Bool
var debugDescription: String {
return "Name: \(self.name) | Favorite?: \(self.isFavorite)"
}
public init(name: String) {
self.name = name
id = UUID()
self.description = "Some text describing why \(self.name.lowercased()) is awesome"
self.isFavorite = false
}
}
class ListItems: ObservableObject {
#Published var items: [ListItem]
let defaultAnimals = ["Ant", "Bear", "Cat", "Dog", "Elephant",
"Fish", "Giraffe", "Hyena", "Iguana", "Jackal", "Kingfisher", "Leopard", "Monkey"]
public init(animals: [String] = []) {
let animalList: [String] = animals.count > 0 ? animals : defaultAnimals
self.items = animalList.sorted {
$0.lowercased() < $1.lowercased()
}.map {
ListItem(name: $0.firstUppercased)
}
}
}
and the following image view in ContentView
struct ContentView: View {
#ObservedObject var list: ListItems = ListItems()
var body: some View {
List(list.items) {
animal in HStack {
// ...
Image(systemName: animal.isFavorite ? "heart.fill" : "heart").foregroundColor(.pink).onTapGesture {
let index = self.list.items.firstIndex { $0.id == animal.id } ?? -1
if (index >= 0) {
self.list.items[index].isFavorite = !animal.isFavorite
self.list.items = Array(self.list.items[0...self.list.items.count-1]) // <--
}
}
// ...
}
}
}
}
Everytime, the image view is tapped, I am basically reassigning the entire array like this so that the changes can be reflected in the UI
self.list.items = Array(self.list.items[0...self.list.items.count-1])
My question: How can I refactor my code to prevent reassigning the entire object array every time some object property changes?
I am fairly new to Swift & iOS development, not sure if I am missing something basic.
Declare ListItem as an struct instead of a class, this way the view will be notified when isFavorite changes. And just a little suggestion; you can use toggle to change the value of a boolean: self.list.items[index].isFavorite.toggle()