I've a SwiftUI-based app that draws into a Canvas for a complicated, dynamic rendering. Since that rendering is based on a map of the world, I transform the provided context to (±180°×±90°) longitude / latitude coordinates before stroking paths etc. Note that the necessary scaling flips the Y-axis because latitude increases up the screen.
All is well until I add words to the picture. Because of the inversion of the Y-axis, the text is rendered inverted.
mercatorContext.draw(Text(satellite.commonName)
.font(Font(.init(.userFixedPitch, size: 4.0)))
.foregroundColor(.white), at: satPoint)
My solution was to draw the text via a another (un-inverted) context which corrects the words, but requires the satPoint to be flipped to place the words at the right place on the (inverted) map ..
With that preamble, someone suggested I apply scaleEffect(y: -1) to the Text and avoid messing with more than one GraphicsContext. This seemed an excellent solution but it doesn't work ..
context.draw(Text(.. draws a Text view but applying scaleEffect turns it into a View which context.draw can't accept. Once it's a View, I suppose I could convert it to an Image and context.draw(Image(.. instead which seems messy.
I wondered about the scaleEffect function .. is it the case that it would ever actually return a view type that was different from the type it was given?
Leaving my curiosity aside, what would a better way than using a second context to keep my text upright?
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Post
Replies
Boosts
Views
Activity
Hello,
I have a view has a textfield.
I want to limit input on it to only letters and put up an alert dialog if anything other than letters.
if anyone give me a good advice about it, I'd be very appreciated.
thanks,
c00012
I am writing an Library app that shows a grid. Each grid location shows the book cover and some info about the book. If the user clicks on a cover, the detail view opens. One option that the user has is a delete the book button. This button deletes the book from SwiftData, but the detail page remains populated. The simple way I want to handle this is to have the delete button also return to the grid view (eg root).
Here is the relative code from the CurrentView
@State private var path = [Book]()
var body: some View {
NavigationStack(path: $path) {
BookListView(sort: sortOrder, searchString: searchText)
.navigationTitle("Library")
.navigationDestination(for: Book.self, destination: EditBookView.init)
.searchable(text: $searchText)
Here is the code from the Grid View
var body: some View {
LazyVGrid (columns: gridLayout) {
ForEach(books) { book in
NavigationLink(value: book) {
GridRow {
VStack {
Here is the code from the Detail View
var body: some View {
Form {
VStack (spacing: 0){
BookImageView(book: book)
}
TextField("Title", text: $book.title)
Button("Go Back to ListView",
action: {
// Action to perform
})
}
.navigationTitle("Edit Book")
.navigationBarTitleDisplayMode(.inline)
} // Form
} // some View
Can you provide the code for the button "Go Back to Last View"
Hello,
In the code below, I want to navigate back to the SecondView when pressing the back button in the ThirdView. However, it navigates to the FirstView instead. How can I make it navigate back to the SecondView without going to the FirstView?
struct FirstView: View {
var body: some View {
NavigationSplitView {
Text("Here is the FirstView")
NavigationLink("Go to SecondView") {
SecondView()
}
} detail: {
EmptyView()
}
}
}
struct SecondView: View {
var body: some View {
Text("Here is the SecondView")
NavigationLink("Go to ThirdView") {
Text("Here is the ThirdView")
}
}
}
I'd like to customise text fields in my application, but I don't see any options for it. When I click text field, new window opens. And there is no title or description.
But there must be something that allows me to customize it, right? Apple applications contain customised text fields.
This is apple's text field (after clicking on it)
This is my text field (after clicking on it)
Here is my code:
Form {
//...
TextField("", text: $address)
//...
}
And there is one more problem. After filling this TextField I click enter and it immediately opens the next TextField. I don't want this behaviour.
I have a simple text field and a button
the textfield text var is binded to my @state string var $strText.
the button has an action that sets strText = ""
when trying to input "nintendo switch" without a capital N, the textField will auto correct to "Nintendo switch". When pressing the button, the textfield will not clear.
I have tried wrapping the button action in a DispatchQueue.main.async but there is no change in behavior.
However, adding an .onSubmit{ } view modifier to the text field and calling strText = "" in the onSubmit closure will clear the textField.
Is there anyway to have the button action call the textfield's submit trigger?
import SwiftUI
struct ContentView: View {
@State var text: String = ""
private func clearText() {
print("YAHOOOO 2clear \(self.text)")
self.text = ""
print("YAHOOOO 2\(self.text)")
}
var body: some View {
VStack {
TextField("Message", text: $text, axis: .vertical)
.autocorrectionDisabled(false)
.onSubmit {
clearText()
}
Button(action: {
DispatchQueue.main.async {
clearText()
}
}, label: {
Text("Send")
})
}
.padding()
}
}
When I initiate WKExtendedRuntimeSession with a background mode of "Underwater Depth" I get an orange indicator showing the Action button on the screen that stays showing all the time and then animates when you press the action button. I believe this started with a recent WatchOS update, because it never happened before.
Does anyone know how to hide it? Or keep it and be able to detect the Action button presses within my app (if possible)?
The Photos app on VisionOS does not apply a blurry navigation bar background to the top of the photos views. Instead if has a transparent navigation bar with some stylized floating buttons.
How can I mimic this in my own SwiftUI VisionOS app?
It is impossible to enter Chinese characters after tokens are inserted into .searchable text field. Input is directly converted into Pinyin alphabets without giving the user a chance to choose its corresponding Chinese character.
If no view is provided in the "token" view builder of the searchable initializer, then this issue does not happen.
I have filed feedback FB13957127. In the meantime, I was wondering if there are any possible workarounds? This bug essentially prevents Chinese users from using the search feature of my app. Thanks!
When I start a workout in my app, the system shows a Live Activity in the Smart Stack like in the picture. My app opens if I tap on it, but the pause and resume buttons doesn't do anything.
I have implemented the PauseWorkoutIntent and ResumeWorkoutIntent which works with the Action Button. I guessed that these would be used from this Live Activity as well, but it seems like not.
Has anyone successfully integrated with this? I haven't seen it documented anywhere, although I think it was already included in watchOS 10.
This is also shown when using the built in workout app and for that the buttons work as expected.
Hi All, this question has been asked before by others, without any success so far.
I'm using the following (standard) method to open the system provided settings screen for my macOS (only) app in SwiftUI.
var body: some Scene {
Settings {
PreferencesView()
}
if #available(macOS 13, *) {
// MenuBarExtra requires macOS 13, but SettingsLink used in here requires macOS 14
MenuBarExtra("...", image: "...") {
...
// this one requires macOS 14
if #available(macOS 14, *) {
SettingsLink() {
Text("Settings...")
}
} else {
Button {
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
} label: {
Image(systemName: "gear")
}
}
}
}
}
I want to know when the settings window closes. onDisappear does not work as the settings window is not really closed, but hidden. So the view stays active. All other hacks I have found neither work.
Does anyone have any idea?
I have a project with two local packages
One client package with an interface and some models with dynamic type in the Package.Swift
One feature package with the UI and a dependency to the client package
When I try to preview a view that is not using any models or code from the client package, it loads just fine e.g. a view that is just a container to display things like a card
But when I tried to preview any other view that actually uses models from the client package, it just fails
the first few lines of the preview error display
LinkDylibError: Failed to build <filename>.swift
Linking failed: linker command failed with exit code 1 (use -v to see invocation)
ld: warning: search path '/Applications/Xcode-15.4.0.app/Contents/SharedFrameworks-iphonesimulator' not found
Undefined symbols for architecture arm64:
Also, I'm using Xcode 15.4 and iOS 17 as the min version
Hi All,
I really need your help, I have been racking my brain to work out why, after a push notification triggers a fetchdata function from the server, my new bookings dont dynamically update the counter against the booking types.
print("Received remote notification: \(userInfo)")
if let dataInfo = userInfo["data"] as? [String: Any],
let jsonData = try? JSONSerialization.data(withJSONObject: dataInfo) {
print("Processing data from notification...")
DispatchQueue.main.async {
self.eventsViewModel.updateFromPushNotification(data: jsonData) { result in
completionHandler(result)
}
}
} else {
print("Failed to parse notification data")
completionHandler(.noData)
}
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register for remote notifications: \(error)")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
print("Will present notification: \(notification.request.content.userInfo)")
DispatchQueue.main.async {
self.eventsViewModel.fetchData()
}
completionHandler([.banner, .badge, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
print("Did receive notification response: \(response.notification.request.content.userInfo)")
let userInfo = response.notification.request.content.userInfo
if let fetchNeeded = userInfo["fetchNeeded"] as? Bool, fetchNeeded {
print("Initiating data fetch due to user interaction...")
DispatchQueue.main.async {
self.eventsViewModel.fetchData()
}
}
completionHandler()
}
@Published var bookings: [AnyBooking] = []
@Published var newBookings: [AnyBooking] = []
@Published var calendarBookings: [String: [AnyBooking]] = [:]
@Published var selectedBooking: AnyBooking?
private var cancellables = Set<AnyCancellable>()
private let calendarManager = CalendarManager.shared // Add calendarManager
func fetchData() {
guard let url = URL(string: "https://allsound.wisewms.uk/webhook_get") else {
print("Invalid URL for webhook request")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in
guard let self = self else { return }
if let error = error {
print("Error fetching data: \(error.localizedDescription)")
return
}
if let data = data, !data.isEmpty {
if let newBookings = self.processBookings(data: data) {
DispatchQueue.main.async {
self.bookings = newBookings
self.separateAndOrganizeBookings(bookings: newBookings)
}
} else {
print("Failed to process bookings.")
}
} else {
print("No data received from server.")
}
}
task.resume()
}
@main
struct AllSoundApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject var eventsViewModel = EventsViewModel()
@Environment(\.scenePhase) var scenePhase
@AppStorage("selectedTheme") private var selectedTheme: Theme = .system
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(eventsViewModel)
.environmentObject(appDelegate.navigationCoordinator)
.preferredColorScheme(selectedTheme.colorScheme)
.onChange(of: scenePhase) { oldPhase, newPhase in
if newPhase == .active {
eventsViewModel.fetchData()
}
}
}
}
}
I have started to work on adding Live Activities to my app in watchOS 11. The app already has interactive Live Activities on the phone that works as expected.
I have added a view for the activityFamily: small and it shows up as expected.
My problem is that buttons doesn't work. I use the Intent based button, and on the iPhone Lock Screen they work. On the watch, nothing happens.
Has anyone got this working? Not sure if I'm missing something or if it's a bug.
I've been seeing warning like the following with my Apple Watch app:
=== AttributeGraph: cycle detected through attribute 140952 ===
=== AttributeGraph: cycle detected through attribute 131640 ===
=== AttributeGraph: cycle detected through attribute 131640 ===
=== AttributeGraph: cycle detected through attribute 131640 ===
=== AttributeGraph: cycle detected through attribute 131640 ===
=== AttributeGraph: cycle detected through attribute 131640 ===
=== AttributeGraph: cycle detected through attribute 131640 ===
I've done some debugging with Instruments and Xcode, I've determined that its caused by adding a
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
Label("Stop", systemImage: "xmark")
}
}
to an item within my NavigationSplitView List Destination view.
To aid reproduction of this issue even more, I've verified the same warning occurs with the Apple sample code from the WWDC23 Backyard Birds app.
https://developer.apple.com/documentation/swiftui/backyard-birds-sample?changes=_9
If you take the Apple sample code, open it in Xcode 15.4 and run it in the Apple Watch WatchOS 10.5 simulator, then select the Bird Springs item, all is well.
If you then add the above toolbar code to the BackyardSummaryTab file at the bottom of the VStack, you'll see the same AttributeGraph issues I'm seeing in my app.
Is this a bug? I can't understand why adding a static ToolBarItem in WatchOS is causing this.
I've seen this issue in the simulator and also on device.
I'm trying to convert my project to use Swift 6 with Complete Concurrency in Xcode 16 beta 1.
The project uses TipKit, but I'm getting compile errors when trying to use the TipKit Parameters feature. Here is an example of the type of error I'm seeing (Note that this code from https://developer.apple.com/documentation/tipkit/highlightingappfeatureswithtipkit):
struct ParameterRuleTip: Tip {
// Define the app state you want to track.
@Parameter
static var isLoggedIn: Bool = false
Static property '$isLoggedIn' is not concurrency-safe because it is non-isolated global shared mutable state.
Is there a new pattern for supporting TipKit Parameters in Swift 6 with Complete Concurrency enabled? There is no obvious suggestion for how to fix this.
The latest WWDC 2024 TipKit doesn't appear to have any solution(s).
Hello there,
I'm playing around with the new ControlWidget controls and I'd like to start a Live Activity from on of them like they did in the WWDC Session.
Is this only possible using push notifications or is there another way around to do so locally?
Thank you.
Hello,
If we have the following code:
import SwiftUI
struct ContentView: View {
@State private var usdAmount = 0.0
var body: some View {
VStack {
Text("Currency Form")
Form {
TextField("Dollar Amount", value: $usdAmount, format: .currency(code: "USD")) // 1
// TextField("Dollar Amount", value: $usdAmount, format: .number) // 2
.keyboardType(.numberPad)
}
Text("usdAmount = \(usdAmount)")
}
.padding()
}
}
When the line marked // 1 is left uncommented (the TF has a .currency() formatter applied) the behaviour of the TF is rather odd.
Upon display it shows US$0.00
Upon editing the value the US$ part remains. So if the user enters US$123 the value is not stored into the usdAmount property.
The User must delete all the existing text and enter e.g. 456.78 and then the value is stored to the usdAmount property.
When Line //1 is commented-out and line //2 is uncommented then the TF behaves correctly because the TF value is just 0 on first display.
Should we not use the .currency() format? Or am I using it wrong?
Thanks.
Hello there,
I'm playing around with the new ControlWidget controls and I'd like to know the best approach to launch a Live Activity using these.
It seems to be possible according to its WWDC session but my main question is: Is it only doable using push notifications?
Thank you.
After 5 years, 6 releases, SwiftUI still miss fundamental feature, lock interface orientation by view (can be by container, modal, …).
Many apps for iPhone just want portrait orientation but in some cases needed a modal in lanscape for a photo viewer or video player. Interface orientation options in info.plist just don’t work because will lock all views in the app.