Post

Replies

Boosts

Views

Activity

Reply to How to draw emojis like the Lock Screen customisation?
Never mind, I figured it out, but it's too much code to post here. But, basically, you plonk an emoji somewhere on the screen, then put six more of them in six positions every 60º around the original one: 0º (top), 60º (top right), 120º (bottom right, 180º (bottom), 240º (bottom left), and 300º (top left). Figure out how the pattern should look, i.e. which of those six are the first character, which are the second, etc. up to seven characters or whatever limit you want. Use the Canvas { context, size in to draw the characters, then snapshot it: let uiImage = MyCanvasView(bgColour: Color.whatever, text: whateverText) .background(Color.whatever) .drawingGroup(opaque: true).snapshot()
4d
Reply to arrays
This function you have is set up to receive an array of Doubles and let you refer to them as costa: func myfunc(costa: [Double]) { // some code } If you're calling the function like this: myfunc(costa: []) then you're passing in an empty array. [] means an empty array. If you want to pass in some values, you need them in the array, e.g.: myfunc(costa: [1.0, 2.1, 3.2, 4.3]) If you need to return an array of Doubles - or anything - you would use a function like this: func myfunc(costa: [Double]) -> [Double] { // some code return myArray // which would be an array of Doubles } A very useful feature of Swift is the ability to hide the name of the variable, so you don't have to enter it every time. Just put an underscore before the first variable name in the function's signature, i.e.: func myfunc(_ costa: [Double]) { // some code } This lets you access the array of Doubles in costa. To call it you would use: myfunc([1.0, 2.1, 3.2, 4.3]) A similar helpful feature is replacing the underscore with something else that makes sense when you read the function out loud. Consider this: func addFive(_ value: Int) -> Int { return value + 5 } You call it with: let total: Int = addFive(6) // 11 You can change the signature to something that makes more sense when read out loud: func addFive(to value: Int) -> Int { return value + 5 } You call it with: let total: Int = addFive(to: 6) // 11 Hope this helps. Also, just an FYI, you're writing in Swift when you do things like this. SwiftUI is for user interface stuff like Buttons, Text, Views, etc.
4d
Reply to Why first View on my NavigationStack appears again when I switch branch?
The NavigationStack still exists because of this sequence of events: You've gone through all the steps in the onboarding and are at step .four. Then, when you click the End button, isFinished is set to true. steps still contains a value, and is not empty until you press the Restart button. If the whole view were refreshed at this point, then SwiftUI would likely not bother to regenerate the NavigationStack because isFinished is not false. As it is, SwiftUI has generated the entire view so that the instant you press Restart, the NavigationStack is present and ready and doesn't need to be generated. Thus, it's all smooth and lovely for your UI and users. As explained, the VStack is not actually displayed on-screen because SwiftUI knows it's not being displayed... yet, but it's there for when it's needed. That's what it looks like anyway. Like I said, raise a bug if you think it's a bug.
4d
Reply to Why first View on my NavigationStack appears again when I switch branch?
It's not really when it appears - as you can see in your example, because the Start text is not actually displayed again. It's when the View is added to the view cycle. So, as I said, the NavigationStack is displayed, you go through the steps, and then there's nothing for it to display but it is still in the view lifecycle until isFinished becomes true. That's why I said it's not really a bug, but you could raise it. Alternatively, someone else who knows what they're talking about could jump in 😃 However, you can use my own code to get around this... enum Step { case one case two case three case four } struct ContentView: View { @State private var isFinished: Bool = false @State private var steps: [Step] = [] var body: some View { if isFinished { Button("Restart") { steps = [] isFinished = false } } else { NavigationStack(path: $steps) { VStack { Text("Start") .onFirstAppear { print("onFirstAppear: start") } Button("Go to step 1") { steps.append(.one) } } .navigationDestination(for: Step.self) { step in switch step { case .one: Button("Go to step 2") { steps.append(.two) } .onFirstAppear { print("onFirstAppear: step 1") } case .two: Button("Go to step 3") { steps.append(.three) } .onFirstAppear { print("onFirstAppear: step 2") } case .three: Button("Go to step 4") { steps.append(.four) } .onFirstAppear { print("onFirstAppear: step 3") } case .four: Button("End") { isFinished = true } .onFirstAppear { print("onFirstAppear: end") } } } } .onFirstAppear { print("onFirstAppear: NavigationStack") } } } } #Preview { ContentView() } extension View { func onFirstAppear(perform: @escaping () -> Void) -> some View { modifier(OnFirstAppear(perform: perform)) } } private struct OnFirstAppear: ViewModifier { let perform: () -> Void @State private var firstTime = true func body(content: Content) -> some View { content.onAppear { if firstTime { firstTime = false perform() } } } }
4d
Reply to Why first View on my NavigationStack appears again when I switch branch?
.onAppear { print("onAppear: start") } is attached to the VStack, so I'd expect that will be displayed regardless of the value in steps. However, even if you attached it instead to the Button inside that VStack I would think it is still displayed because it's in the view until isFinished becomes true. When you get to step four, press the End button and set steps = [], at that point the navigation stack has nothing in the switch statement so it's going to display the VStack, but then the view is redrawn because finished is now true. I'm not sure it's a bug, but you could raise it and Apple might be able to re-jig the inner workings so that part of your if statement is completely ignored when finished becomes true.
4d
Reply to Common blocks in Swift?
#define COM_OFFSET 12.5 is simply let COM_OFFSET = 12.5. It looks like you want a file of commonly-used values/constants. That's easy. Just create a new Swift file and put your declarations inside, e.g.: let COM_OFFSET = 12.5 // You don't have to give arrays an initial size, but don't make them a `let` because you can't then add anything to it // So, either create an empty array globally like this, and add entries somewhere else, like: var dates: [PCDate] = [] addToDates() func addToDates() { dates.append(Date.now) dates.append(Date.now.advanced(by: 60) } // Or you can create a function that populates it when you create it, e.g. var dates = initialiseDates() func initialiseDates() -> [Date] { var array: [Date] = [] for index in 1...3 { array.append(Date.now) } return array } For the class stuff, just read up on how to create classes in Swift, but it's very easy - and you should put classes in their own file, so: import Foundation class MyLogger { static let fileURL: URL = { FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "my.app.group")!.appendingPathComponent("main.log") }() ... Then you just create an instance of it like any other class, just like you did with var dates: [Date] = [] above, i.e. var logger: MyLogger.
5d
Reply to Shortcut automation
What do you think we can do with this? You haven't bothered to say what you were doing, what automation you're trying to use or create. These are the Developer Forums, where developers of apps for Apple's platforms ask each other for hints and tips on coding. Are you in the right place?
5d
Reply to Refreshing widgets - policy and background tasks?
I've extensivley tested this over the past two days. I tried timelines spanning one day, with: 72 entries, every 20 minutes 36 entries, every 40 minutes 24 entries, every hour 12 entries, every 2 hours 6 entries, every 4 hours 3 entries, every 8 hours 2 entries, every 12 hours In every case, the policy is .atEnd, and in every case the timeline is not refreshed when it's supposed to be, which causes the widget to display the wrong timer values. Due to the inflexibility of Text(date, style: .timer), I need to write code that calculates the correct timer value but shows less than a day. For example, if it's 11am today and the event date is tomorrow at noon, there are 25 hours between the two dates and the standard .timer would show 25:00:00, which is not what I want. So, I calculate the next occurrence of noon, and the widget shows "1 day" with a timer of "1:00:00". Every timeline with a refresh policy of .atEnd caues the countdown timer to show the wrong values. I've got it logging to a file, and I can see that the countdown dates are completely correct in every case. The widget is never refreshed at the right time, so the timers always show the wrong values. The only way I've managed to get this to work is to have a timeline with one entry, and a refresh policy of .after(), i.e. let date = Date() let refreshDate = Calendar.autoupdatingCurrent.date(byAdding: .minute, value: 30, to: date)! Timeline(entries: entries, policy: .after(refreshDate)) There is clearly something wrong with .atEnd.
6d