Provide views, controls, and layout structures for declaring your app's user interface using SwiftUI.

SwiftUI Documentation

Posts under SwiftUI tag

2,332 Posts
Sort by:
Post not yet marked as solved
0 Replies
106 Views
Hello, fairly new to Swift, I come from a React Native background. One of the hardest things I'm finding is simply customising the screen headers in the navigation. I've managed to do it using a custom modifier that uses .toolbar and ToolbarItem as shown below: struct NavBar: ViewModifier { let title: String let showBackButton: Bool? @Environment(\.dismiss) private var dismiss func body(content: Content) -> some View { return content .toolbar { if showBackButton == true { ToolbarItemGroup(placement: .navigationBarLeading) { Button(action: { dismiss() }) { Image("BackButton") }.padding(.top, 18) } } ToolbarItem(placement: .principal) { Text(title) .font(Font.custom("Knight Vision", size: 28)) .foregroundColor(.white).padding(.top, 20) } } .navigationBarBackButtonHidden(true) } } This is all fine and suits my needs however I'm finding that the toolbar does not slide in with the rest of the screen when navigating to as screen with it on. I would expect the title to slide in with the other items on the screen. Especially since the toolbar does animate out, it just does not animate in. Heres a video so you can see what I mean. Am I doing something wrong here? Is there a better way to do this? [linkText](https://www.youtube.com/shorts/6M-glapBZz0 /)
Posted
by
Post not yet marked as solved
0 Replies
123 Views
UPDATED: I determined the line causing the hang was .animation(.default, value: layout). I'm seeing a strange issue when switching between a ScrollView/LazyVGrid and a List with my SwiftData but when toggling the layout it ends up freezing and I can't confirm what's causing the app to hang since there's no crash. I'm not getting much info from the debugger. Any help would be appreciated. struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var items: [Item] let gridItemLayout = [ GridItem(.adaptive(minimum: 150))] @State private var layout = Layout.grid var body: some View { NavigationStack { ZStack { if layout == .grid { ScrollView { LazyVGrid(columns: gridItemLayout, spacing: 5) { ForEach(items) { item in } } } } else { List { ForEach(items) { item in } } } } // MARK: HERE'S THE ERROR .animation(.default, value: layout) .navigationTitle("ScrollView") .toolbar { ToolbarItemGroup { Button(action: addItem) { Label("Add Item", systemImage: "plus") } Menu { Picker("Layout", selection: $layout) { ForEach(Layout.allCases) { option in Label(option.title, systemImage: option.imageName) .tag(option) } } .pickerStyle(.inline) } label: { Label("Layout Options", systemImage: layout.imageName) .labelStyle(.iconOnly) } } } } } } @Model public class Item: Codable { public let id: String public enum CodingKeys: String, CodingKey { case id } public init(id: String) { self.id = id } required public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(String.self, forKey: .id) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) } }
Posted
by
Post not yet marked as solved
1 Replies
138 Views
When I run the code below, the trace, "Called", is shown 3-4 times initially. If I click on a color row, the trace shows 9 times. Why is that? If I comment out the line, @Environment(\.dismiss) private var dismiss, the trace shows only 1 time, as expected. I've read a number of reports regarding dismiss() which seems to be very brittle. It often causes an infinite loop. But I need to dismiss a view. Its older counterpart, @Environment(\.presentationMode), seems to cause infinite loop at times. Are there other ways to dismiss a view without suffering these issues? struct TestNavigationLink: View { @Environment(\.dismiss) private var dismiss var body: some View { let _ = print("Called") NavigationStack { List { NavigationLink("Mint") { ColorDetail(color: .mint) } } .navigationTitle("Colors") } } // body struct ColorDetail: View { var color: Color var body: some View { color.navigationTitle(color.description) } } }
Posted
by
Post not yet marked as solved
1 Replies
115 Views
Fairly new to SwiftUI and using it for a school project. I keep getting this error and I have no idea how to fix it. // AddToCartButton.swift // ProducePal // import SwiftUI struct AddToCartButton: View { @EnvironmentObject var multicart: MultiCartManager var product: Product var body: some View { VStack { Button { multicart.addToCart2(product: product) } label: { Text("Add to Cart") .font(.headline) .fontWeight(.semibold) .foregroundColor(.white) .padding() .padding(.horizontal, 20) .background(Color.blue) .cornerRadius(10) .shadow(radius: 20) } } .buttonStyle(PlainButtonStyle()) } } struct AddToCartButton_Previews: PreviewProvider { static var previews: some View { AddToCartButton(product: productList[0]) .environmentObject(MultiCartManager()) } }
Posted
by
Post not yet marked as solved
2 Replies
103 Views
It seems that when you tested the official code today, you found that screen reading works fine with English characters, but when using Chinese characters, the Text and Button elements cannot be read correctly.. It's important to address this issue to bring convenience to your blind friends. The official code address is:https://developer.apple.com/documentation/swiftui/creating_accessible_views.
Posted
by
Post not yet marked as solved
0 Replies
123 Views
Hello everyone, I looked at various methods how to Unit/UITest SwiftData but I couldn't find something simple. Is it even possible to test SwiftData? Does someone found a solution for that?
Posted
by
Post not yet marked as solved
1 Replies
148 Views
Hello everyone My goal is to create Apple's activity ring sparkle effect. So I found Paul Hudson's Vortex library. There is already a sparkle effect, but I don't know how to modify it to achieve my goal. Because I'm pretty new to SwiftUI animations. Does anyone have any idea how I could do this? Vortex project: https://github.com/twostraws/Vortex
Posted
by
Post not yet marked as solved
0 Replies
140 Views
After a while I returned to Xcode . I bought a new Mac mini M2 and I installed the Xcode 15.3. To familiarize with the changes in the plataform I follow the tutorial you provide. I create a new iOS app following the instructions. I clicked in the ContentView and I was supposed to see in the canvas a representation of an iPhone running my app. Insteady I saw an error message "Cannot preview in this file - Unexpected error ocurred". I'm blocked in this situation . Do you have a solution for that? The code is provided by your tutorial. I didn't change it. Thanks for your help in this matter. Fernando
Posted
by
Post not yet marked as solved
1 Replies
164 Views
I have a situation where tapping on a NavigationLink on an item from a SwiftData Query results in an infinite loop, causing the app the freeze. If you run the code, make sure to add at least 1 item, then tap on it to see the issue. Here is the code for a small sample app I made to illustrate the issue: import SwiftUI import SwiftData @main struct TestApp: App { var sharedModelContainer: ModelContainer = { let schema = Schema([ Item.self ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { let container = try ModelContainer(for: schema, configurations: [modelConfiguration]) return container } catch { fatalError("Could not create ModelContainer: \(error)") } }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) } } struct ContentView: View { var body: some View { NavigationStack { ListsView() } } } struct ListsView: View { @Environment(\.modelContext) private var modelContext @Query(filter: #Predicate<Item> { _ in true }) private var items: [Item] var body: some View { List(items) { item in NavigationLink { ItemDetail() } label: { VStack { Text("\(item.name) | \(item.date.formatted())") } } } Button("Add") { let newItem = Item(name: "Some item", date: .now) modelContext.insert(newItem) try? modelContext.save() } } } struct ItemDetail: View { private var model = ItemModel() var body: some View { VStack { Text("detail") } } } fileprivate var count = 0 class ItemModel { var value: Int init() { value = 99 print("\(count)") count += 1 } } @Model final class Item { let name: String let date: Date init(name: String, date: Date) { self.name = name self.date = date } } In the test app above, the code in the initializer of ItemModel will run indefinitely. There are a few things that will fix this issue: comment out the private var model = ItemModel() line in ItemDetail view replace the @Query with a set list of Items move the contents of the ListsView into the ContentView instead of referencing ListsView() inside the NavigationStack But I'm not sure why this infinite loop is happening with the initializer of ItemModel. It seems like a SwiftData and/or SwiftUI bug, because I don't see a reason why this would happen. Any ideas? Has anyone run into something similar?
Posted
by
Post not yet marked as solved
0 Replies
99 Views
Hi, I have a List and I want to limit the dynamic text size for some of the elements in the list's row item view. I created a test view below. The ".dynamicTypeSize(.large)" restriction only works if it's applied to the List view, not if it's set for the the ContentItemView in the ForEach below. Is there a reason for this? Do I need to do something else to limit a list row to a certain size? The example only has a text field, but I want to do this for a Image with some text inside it, and I wanted to restrict that text field, but it doesn't seem to work when the view is inside a List row. Please let me know if there's a workaround for it. import SwiftUI import CoreData struct ContentView: View { @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], animation: .default) private var items: FetchedResults<Item> @State private var multiSelectedContacts = Set<Item.ID>() var body: some View { NavigationStack { List (selection: $multiSelectedContacts) { ForEach(items) { item in ContentItemView(item: item) } .dynamicTypeSize(.large) // <-- doesn't works } .dynamicTypeSize(.large) // <-- THIS WORKS } } } struct ContentItemView: View { @Environment(\.managedObjectContext) private var viewContext @ObservedObject var item: Item @State var presentConfirmation = false var body: some View { HStack { if let timestamp = item.timestamp, let itemNumber = item.itemNumber { Text("\(itemNumber) - \(timestamp, formatter: itemFormatter)") } } .popover(isPresented: $item.canShowPopover, content: { Text("Test Item Label") .frame(width: 100, height: 150) }) } } private let itemFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .long return formatter }() #Preview { ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) }
Posted
by
Post not yet marked as solved
1 Replies
124 Views
Hi everyone, I’m just starting with swift and Xcode and have a basic question. I have the following code I found online for an app that generates math addition questions. I would like to run this Math app on my iPhone just before I open the apps I use most often (let’s say mail, WhatsApp, calendar and notes) ask me a maths question and if I answer correctly, carryon with the app originally intended to be opened. I can do the opening of the Math app before the apps I use more often with shortcuts. I would like to modify the code bellow so that if I answer correctly it “closes” itself and returns to the originally intended app. With that intention I included the “exit(0)”, but I get an error. Thanks for your help in advance! Best, Tom struct ContentView: View { @State private var correctAnswer = 0 @State private var choiceArray : [Int] = [0, 1, 2, 3] @State private var firstNumber = 0 @State private var secondNumber = 0 @State private var difficulty = 1000 var body: some View { VStack { Text("(firstNumber) + (secondNumber)") .font(.largeTitle) .bold() HStack { ForEach(0..<2) {index in Button { answerIsCorrect(answer: choiceArray[index]) generateAnswers() } label: { AnswerButton(number: choiceArray[index]) } } } HStack { ForEach(2..<4) {index in Button { answerIsCorrect(answer: choiceArray[index]) generateAnswers() } label: { AnswerButton(number: choiceArray[index]) } } } } func answerIsCorrect(answer: Int){ if answer == correctAnswer {exit(0)} } } func generateAnswers(){ firstNumber = Int.random(in: 0...(difficulty/2)) secondNumber = Int.random(in: 0...(difficulty/2)) var answerList = Int correctAnswer = firstNumber + secondNumber for _ in 0...2 { answerList.append(Int.random(in: 0...difficulty)) } answerList.append(correctAnswer) choiceArray = answerList.shuffled() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
Posted
by
Post not yet marked as solved
0 Replies
103 Views
I have a data object that dynamically changes the UIImage assigned to one of its instance variables, but when showing this image in SwiftUI, it's always black and white. The following sample code shows the difference between the same image, but using first the native constructor Image(systemName:) and then Image(uiImage:). When using AppKit and Image(nsImage:) this issue doesn't happen. import SwiftUI import UIKit struct ContentView: View { @State var object = MyObject() var body: some View { Image(systemName: "exclamationmark.triangle.fill") .symbolRenderingMode(.palette) .foregroundStyle(.white, .yellow) Image(uiImage: object.image) } } class MyObject { var image = UIImage(systemName: "exclamationmark.triangle.fill")! .applyingSymbolConfiguration(.init(paletteColors: [.white, .systemYellow]))! } #Preview { ContentView() }
Posted
by
Post not yet marked as solved
0 Replies
90 Views
I'm trying to add a SpriteKit scene to my view using SpriteView(scene: spriteScene). But it's doesn't work the .sks scene doesn't display here is the image. (and this is the code :) import SwiftUI import _SpriteKit_SwiftUI struct Gameplay: View { let spriteScene: MainGameScene var body: some View { ZStack { ----------------------- SpriteView(scene: spriteScene) ---------------------------- } } }
Posted
by
Post not yet marked as solved
2 Replies
123 Views
Is there a way to modify the SignInWithAppleButton so that only the Apple logo appears? According to Apple's guidelines, it's permissible to use only the icon. https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple Unfortunately, the button isn't customizable. However, I need a button that utilizes the logic of onRequest and onCompletion. Here's my current code using the standard SignInWithAppleButton. SignInWithAppleButton(.signIn, onRequest: { request in print("Apple ID Request") AppleSignInManager.shared.requestAppleAuthorization(request) }, onCompletion: { result in print("Apple ID Completion") handleAppleID(result) } ) .font(.title) .signInWithAppleButtonStyle(.white) .frame(height: 50) I tried exploring various SwiftUI customization options for the SignInWithAppleButton, such as adjusting its style or overlaying it with a custom image. I was expecting to find a way to remove the text and display only the Apple logo, as permitted by Apple's guidelines. However, I found that the SignInWithAppleButton isn't easily customizable in this way. So, I'm seeking guidance on alternative approaches to achieve the desired customization while still maintaining the functionality provided by the onRequest and onCompletion handlers.
Posted
by
Post marked as solved
2 Replies
154 Views
Hi team, I'm running into the following issue, for which I don't seem to find a good solution. I would like to be able to drag and drop items from a view into empty space to open a new window that displays detailed information about this item. Now, I know something similar has been flagged already in this post (FB13545880: Support drag and drop to create a new window on visionOS) HOWEVER, all this does, is launch the App again with the SAME WindowGroup and display ContentView in a different state (show a selected product e.g.). What I would like to do, is instead launch ONLY the new WindowGroup, without a new instance of ContentView. This is the closest I got so far. It opens the desired window, but in addition it also displays the ContentView WindowGroup WindowGroup { ContentView() .onContinueUserActivity(Activity.openWindow, perform: handleOpenDetail) } WindowGroup(id: "Detail View", for: Reminder.ID.self) { $reminderId in ReminderDetailView(reminderId: reminderId! ) } .onDrag({ let userActivity = NSUserActivity(activityType: Activity.openWindow) let localizedString = NSLocalizedString("DroppedReminterTitle", comment: "Activity title with reminder name") userActivity.title = String(format: localizedString, reminder.title) userActivity.targetContentIdentifier = "\(reminder.id)" try? userActivity.setTypedPayload(reminder.id) // When setting the identifier let encoder = JSONEncoder() if let jsonData = try? encoder.encode(reminder.persistentModelID), let jsonString = String(data: jsonData, encoding: .utf8) { userActivity.userInfo = ["id": jsonString] } return NSItemProvider(object: userActivity) }) func handleOpenDetail(_ userActivity: NSUserActivity) { guard let idString = userActivity.userInfo?["id"] as? String else { print("Invalid or missing identifier in user activity") return } if let jsonData = idString.data(using: .utf8) { do { let decoder = JSONDecoder() let persistentID = try decoder.decode(PersistentIdentifier.self, from: jsonData) openWindow(id: "Detail View", value: persistentID) } catch { print("Failed to decode PersistentIdentifier: \(error)") } } else { print("Failed to convert string to data") } }
Posted
by
Post not yet marked as solved
2 Replies
119 Views
Is there a system deep link URI to the built in files app? I would like to direct users to my apps location in the files app. For example files://myApp The only exposed deep links for system I can find are the ones for mail, sms, FaceTime etc. Thank you (tag used for post was because I couldn’t find a deep link tag)
Posted
by
Post marked as solved
3 Replies
138 Views
Is it possible to switch to a new View without using NavigationStack or NavigationLink or NavigationView? I know I can do it with a Bool, which either shows the second view, or the first, and then toggles the Bool. But can't I do something like this? Which obviously doesn't work. struct BasicButton: View { var buttonLabel = "Create User" var body: some View { Button { CreateUser() //another SwiftUI view, not a function } label: { Text(buttonLabel) } } }
Posted
by
Post not yet marked as solved
0 Replies
82 Views
When I try to share a image of a component using the ImageRenderer, the type is not .png while sharing the image created using ShareLink (The type is .jpeg for some reasons...) My code looks like this: ShareLink( "Share", item: ( ImageRenderer( content: shareView.frame( width: 420, height: 520 ) ).uiImage?.pngData() )!, preview: SharePreview( "Share Preview", image: ( ImageRenderer( content: shareView.frame( width: 420, height: 520 ) ).uiImage?.pngData() )! ) ) Also the image shared is always in low resolution, if anyone knows what to do in this case let me know! Any helps will be appreciated!
Posted
by
Post not yet marked as solved
0 Replies
95 Views
I have a Canvas inside a ScrollView on a Mac. The Canvas's size is determined by a model (for the example below, I am simply drawing a grid of circles of a given radius). Everything appears to works fine. However, I am wondering if it is possible for the Canvas rendering code to know what portion of the Canvas is actually visible in the ScrollView? For example, if the Canvas is large but the visible portion is small, I would like to avoid drawing content that is not visible. Is this possible? Example of Canvas in a ScrollView I am using for testing: struct MyCanvas: View { @ObservedObject var model: MyModel var body: some View { ScrollView([.horizontal, .vertical]) { Canvas { context, size in // Placeholder rendering code for row in 0..<model.numOfRows { for col in 0..<model.numOfColumns { let left: CGFloat = CGFloat(col * model.radius * 2) let top: CGFloat = CGFloat(row * model.radius * 2) let size: CGFloat = CGFloat(model.radius * 2) let rect = CGRect(x: left, y: top, width: size, height: size) let path = Circle().path(in: rect) context.fill(path, with: .color(.red)) } } } .frame(width: CGFloat(model.numOfColumns * model.radius * 2), height: CGFloat(model.numOfRows * model.radius * 2)) } } }
Posted
by