I added a canvas view using PDFPageOverlayViewProvider. When I zoom the PDFView, the drawing is scaled, but its quality becomes blurry. How can I fix this?
import SwiftUI
import PDFKit
import PencilKit
import CoreGraphics
struct ContentView: View {
var body: some View {
if
let url = Bundle.main.url(forResource: "sample", withExtension: "pdf"),
let data = try? Data(contentsOf: url),
let document = PDFDocument(data: data)
{
PDFRepresentableView(document: document)
} else {
Text("fail")
}
}
}
#Preview {
ContentView()
}
struct PDFRepresentableView: UIViewRepresentable {
let document: PDFDocument
let pdfView = PDFView()
func makeUIView(context: Context) -> PDFView {
pdfView.displayMode = .singlePageContinuous
pdfView.usePageViewController(false)
pdfView.displayDirection = .vertical
pdfView.pageOverlayViewProvider = context.coordinator
pdfView.document = document
pdfView.autoScales = false
pdfView.minScaleFactor = 0.7
pdfView.maxScaleFactor = 4
return pdfView
}
func updateUIView(_ uiView: PDFView, context: Context) {
// Optional: update logic if needed
}
func makeCoordinator() -> CustomCoordinator {
return CustomCoordinator(parent: self)
}
}
class CustomCoordinator: NSObject, PDFPageOverlayViewProvider, PKCanvasViewDelegate {
let parent: PDFRepresentableView
init(parent: PDFRepresentableView) {
self.parent = parent
}
func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? {
let result = UIView()
let canvasView = PKCanvasView()
canvasView.drawingPolicy = .anyInput
canvasView.tool = PKInkingTool(.pen, color: .blue, width: 20)
canvasView.translatesAutoresizingMaskIntoConstraints = false
result.addSubview(canvasView)
NSLayoutConstraint.activate([
canvasView.leadingAnchor.constraint(equalTo: result.leadingAnchor),
canvasView.trailingAnchor.constraint(equalTo: result.trailingAnchor),
canvasView.topAnchor.constraint(equalTo: result.topAnchor),
canvasView.bottomAnchor.constraint(equalTo: result.bottomAnchor)
])
for subView in view.documentView?.subviews ?? [] {
subView.isUserInteractionEnabled = true
}
result.layoutIfNeeded()
return result
}
}
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Posts under SwiftUI tag
200 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
I implemented consumable in-app purchases in an iPhone app using ProductView().
When I tap the payment button in ProductView(), I am taken to the payment screen and once the payment is completed the next code seems to be executed, so there doesn't seem to be a problem, but if I tap the payment button in ProductView() again, the next code is executed without taking me to the payment screen.
This means that a single payment can be made multiple times.
Can someone help?
ProductView(id: "geminiOneMatch")
.productViewStyle(.compact)
.padding()
.onInAppPurchaseCompletion { product, result in
if case .success(.success(_)) = result {
// 課金が成功した場合の処理
gemini.addOneMatch(amount: 20)
popUpVM.geminiOneMatchPopUp = false
dataManageVM.generateRespons(locale: locale)
}
}
This is obviously a user error, but I've been working on different possibilities for a couple of days and nothing works. The problem is my Section in the following code doesn't expand or collapse when I click on the chevron:
`class AstroCat {
var title: String
var contents: [ String ]
var isExpanded: Bool
init(title: String, contents: [String], isExpanded: Bool) {
self.title = title
self.contents = contents
self.isExpanded = isExpanded
}
}
struct TestView: View {
@Binding var isShowingTargetSelection: Bool
@State var catalog: AstroCat
@State private var expanded = false
var body: some View {
NavigationStack {
List {
Section(catalog.title, isExpanded: $catalog.isExpanded) {
ForEach(catalog.contents, id: \.self) { object in
Text(object)
}
}
}
.navigationTitle("Target")
.listStyle(.sidebar)
}
}
}
#Preview {
struct TestPreviewContainer : View {
@State private var value = false
@State var catalog = AstroCat(title: "Solar System", contents: ["Sun", "Mercury", "Venus", "Earth"], isExpanded: true)
var body: some View {
TestView(isShowingTargetSelection: $value, catalog: catalog)
}
}
return TestPreviewContainer()
}`
If I change the "isExpanded: $catalog.isExpanded" to just use the local variable "expanded", then it works, so I think I have the basic SwiftUI pieces correct. But using a boolean inside of the class doesn't seem to work (the section just remains expanded or collapsed based on the initial value of the class variable).
Any hints? Am I not specifying the binding correctly? (I've tried a bunch of alternatives)
Thanks for the help,
Robert
In TabView, when I open a view in a Tab, and I switch to another Tab, but the View lifecycle of the view in the old Tab is still not over, and the threads of some functions are still in the background. I want to completely end the View lifecycle of the View in the previously opened tab when switching Tab. How can I do it? Thank you!
Although it doesn't seem to be a forbidden practice, placing toolbar items in the bottom bar of a modal Sheet (which has its own NavigationStack) triggers massive layout warnings.
The same thing occurs when using the .searchable(...) view modifier inside a Sheet (which affects the bottom bar too).
LayoutWarning.txt
I want to make an agent application in SwiftUI.
It would be setup as a login item and have no dock icon and display no windows on start.
I used AlertScene at first, but it is unavailable on macOS 14 which is my deployment target.
Is there a way to have no windows or I'd better use AppKit for that?
[Submitted as FB18870294, but posting here for visibility.]
In iOS 26 beta 3 (23A5287g), implicit animations no longer work when conditionally showing or hiding rows in a Form.
Rows with Text or other views inside a Section appear and disappear abruptly, even when wrapped in withAnimation or using .animation() modifiers. This is a regression from iOS 18.5, where the row item animates in and out correctly with the same code.
Repro Steps
Create a new iOS App › SwiftUI project.
Replace its ContentView struct with the code below
Build and run on an iOS 18 device.
Tap the Show Middle Row toggle and note how the Middle Row animates.
Build and run on an iOS 26 beta 3 device.
Tap the Show Middle Row toggle.
Expected
Middle Row item should smoothly animate in and out as it does on iOS 18.
Actual
Middle Row item appears and disappears abruptly, without any animation.
Code
struct ContentView: View {
@State private var showingMiddleRow = false
var body: some View {
Form {
Section {
Toggle(
"Show **Middle Row**",
isOn: $showingMiddleRow.animation()
)
if showingMiddleRow {
Text("Middle Row")
}
Text("Last Row")
}
}
}
}
I need a layout where I have a ScrollView with some content, and ScrollView has full screen background image. Screen is pushed as detail on stack.
When my screen is pushed we display navigation bar. We want a new scrollEdgeEffectStyle .soft style work. But when we scroll the gradient blur effect bellow bars is fixed to top and bottom part of the scroll view background image and is not transparent. However when content underneath navigation bar is darker and navigation bar changes automatically to adapt content underneath the final effect looks as expected doesn't use background image.
Expected bahaviour for us is that the effect under the navigation bar would not use background image but would be transparent based on content underneath.
This is how it is intialy when user didn't interact with the screen:
This is how it looks when user scrolls down:
This is how it looks when navigation bar adapts to dark content underneath:
Minimal code to reproduce this behaviour:
import SwiftUI
@main
struct SwiftUIByExampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
NavigationStack {
ScrollView(.vertical) {
VStack(spacing: 0.0) {
ForEach(1 ..< 101, id: \.self) { i in
HStack {
Text("Row \(i)")
Spacer()
}
.frame(height: 50)
.background(Color.random)
}
}
}
.scrollEdgeEffectStyle(.soft, for: .all)
.scrollContentBackground(.hidden)
.toolbar {
ToolbarItem(placement: .title) {
Label("My Awesome App", systemImage: "sparkles")
.labelStyle(.titleAndIcon)
}
}
.toolbarRole(.navigationStack)
.background(
ZStack {
Color.white
.ignoresSafeArea()
Image(.sea)
.resizable()
.ignoresSafeArea()
.scaledToFill()
}
)
}
}
}
extension Color {
static var random: Color {
Color(
red: .random(in: 0...1),
green: .random(in: 0...1),
blue: .random(in: 0...1)
)
}
}
We've also tried using ZStack instead of .background modifier but we observed the same results.
We want to basically achieve the same effect as showcased here, but with the static background image:
https://youtu.be/3MugGCtm26A?si=ALG29NqX1jAMacM5&t=634
I've observed a difference in the layout of menu items within ContextMenu and Menu when comparing system applications to my own SwiftUI app, specifically concerning the order of icons and titles.
On iOS 26, system apps (as shown in the image in the "System App" column) appear to display the item's icon before its title for certain menu items. However, in my SwiftUI app, when I use a Label (e.g. Label("Paste", systemImage: "doc.on.clipboard")) or an HStack containing an Image and Text, the icon consistently appears after the title within both ContextMenu and Menu items.
I'm aiming to achieve the "icon first, then title" layout as seen in system apps. My attempts to arrange this order using HStack directly within the Button's label closure:
Menu {
Button { /* ... */ } label: {
HStack {
Image(systemName: "doc.on.clipboard")
Text(String(localized: "Paste"))
}
}
// ...
} label: {
Text("タップミー")
}
seem to be overridden or restricted by the OS, which forces the icon to the leading position (as shown in the image in the "Custom App" column).
System App
Custom App
Is there a specific SwiftUI modifier, or any other setting I might have overlooked that allows developers to control the icon/title order within ContextMenu or Menu items to match the system's behavior? Or is this a system-controlled layout that app developers currently cannot customize, and we need to wait for potential changes from Apple to expose this capability for in-app menus?
Thanks in advance!
Buttons placed in the bottomBar and keyboard toolbar item positions in an inspector appear disabled/grayed out when at the large presentation detent.
The same is not true for sheets.
Is this intentional or a bug? If intentional, is there any backing design theory in the Human Interface Guidelines for it?
Xcode 16.4 / 18.5 simulator
// Inspector @ large detent
struct ContentView: View {
var body: some View {
Color.clear
.inspector(isPresented: .constant(true)) {
Color.clear
.presentationDetents([.large])
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("Save") {}
.border(.red)
}
}
}
}
}
// Sheet
struct ContentView: View {
var body: some View {
Color.clear
.sheet(isPresented: .constant(true)) {
Color.clear
.presentationDetents([.medium])
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("Save") {}
.border(.red)
}
}
}
}
}
Hello,
Thank you for your time. I have a question regarding visionOS app development.
When placing a SwiftUI TextField inside RealityView.attachments, we found that focusing on the field does not bring up the virtual keyboard in front of the user. Instead, the keyboard appears around the user’s lower abdomen area.
However, when placing the same TextField in a regular SwiftUI layer outside of RealityView, the keyboard appears in the correct position as expected. This suggests that the issue is specific to RealityView.attachments.
We are currently exploring ways to have the virtual keyboard appear directly in front of the user when using TextField inside RealityViewAttachments. If there is any method to explicitly control the keyboard position or any known workarounds—including alternative UI approaches—we would greatly appreciate your guidance.
Best regards,
Sadao Tokuyama
Error:
Fatal error: init(coder:) has not been implemented
File:
UIKitCore/UICoreHostingView.swift:54
Stack Trace Snippet:
swift
Copy
Edit
UIKitCore/UICoreHostingView.swift:54: Fatal error: init(coder:) has not been implemented
Can't show file for stack frame: <DBGLldbStackFrame: 0x10ca74560> - stackNumber: 23
name: @objc ThemeableViewController.init(coder:).
The file path does not exist on the file system: /
I am encounter an issue with the height of a ScrollView not rendering properly during the transition of a sheet from closed to open. This results in a gap between the bottom edge of the ScrollView and the bottom edge of the sheet during the animation. I am getting this issue when trying to use the ScrollView inside a NavigationStack and when using a PresentationDetent other than .large.
The code snippet below, for example, suffers from the issue.
ScrollView {
Button("Reveal sheet") {
isPresented = true
}
}
.frame(maxWidth: .infinity)
.background(.yellow)
.sheet(isPresented: $isPresented) {
VStack {
NavigationStack {
ScrollView {
ForEach(0..<100, id: \.self) { number in
Text("\(number)")
}
.frame(maxWidth: .infinity)
}
.background(.green)
.presentationDetents([.medium])
}
}
}
Here is what the issue looks like for this example.
The issue occurs in:
Simulator iPhone 16 iOS 18.4
Personal device (iPhone 16 iOS 18.4)
Canvas preview
If you are currently on the beta of iOS 26, open Apple Music and you'll see a tabViewBottomAccessory that is the mini NowPlayingView. When tapped, it opens the NowPlayingView. Is there a similar way to do this in SwiftUI?
Looking through Apple's documentation, they do not specify any way to reproduce the same kind of view transition.
This is the Apple Music app with the tabViewBottomAccessory. When clicked it opens the NowPlayingView
I'm implementing an App Intent for my iOS app that helps users plan trip activities. It only works when run as a shortcut but not using voice through Siri. There are 2 issues:
The ShortcutsTripEntity will only accept a voice input for a specific trip but not others.
I'm stuck with a throwing error when trying to use requestDisambiguation() on the activity day @Parameter property.
How do I rectify these issues.
This is blocking me from completing a critical feature that lets users quickly plan activities through Siri and Shortcuts.
Expected behavior for trip input: The intent should make Siri accept the spoken trip input from any of the options.
Actual behavior for trip input: Siri only accepts the same trip when spoken but accepts any when selected by click/touch.
Expected behavior for day input: Siri should accept the spoken selected option.
Actual behavior for day input: Siri only accepts an input by click/touch but yet throws an error at runtime I'm happy to provide more code. But here's the relevant code:
struct PlanActivityTestIntent: AppIntent {
@Parameter(title: "Activity Day")
var activityDay: ShortcutsItineraryDayEntity
@Parameter(
title: "Trip",
description: "The trip to plan an activity for",
default: ShortcutsTripEntity(id: UUID().uuidString, title: "Untitled trip"),
requestValueDialog: "Which trip would you like to add an activity to?"
)
var tripEntity: ShortcutsTripEntity
@Parameter(title: "Activity Title", description: "The title of the activity", requestValueDialog: "What do you want to do or see?")
var title: String
@Parameter(title: "Activity Day", description: "Activity Day", default: ShortcutsItineraryDayEntity(itineraryDay: .init(itineraryId: UUID(), date: .now), timeZoneIdentifier: "UTC"))
var activityDay: ShortcutsItineraryDayEntity
func perform() async throws -> some ProvidesDialog {
// ...other code...
let tripsStore = TripsStore()
// load trips and map them to entities
try? await tripsStore.getTrips()
let tripsAsEntities = tripsStore.trips.map { trip in
let id = trip.id ?? UUID()
let title = trip.title
return ShortcutsTripEntity(id: id.uuidString, title: title, trip: trip)
}
// Ask user to select a trip. This line would doesn't accept a voice // answer. Why?
let selectedTrip = try await $tripEntity.requestDisambiguation(
among: tripsAsEntities,
dialog: .init(
full: "Which of the \(tripsAsEntities.count) trip would you like to add an activity to?",
supporting: "Select a trip",
systemImageName: "safari.fill"
)
)
// This line throws an error
let selectedDay = try await $activityDay.requestDisambiguation(
among: daysAsEntities,
dialog:"Which day would you like to plan an activity for?"
)
}
}
Here are some related images that might help:
The following repro case results in a previews crash on Xcode 26 beta 3 (report attached). FB18762054
import SwiftUI
final class MyItem: Identifiable, Labelled {
var label: String
init(_ label: String) {
self.label = label
}
}
protocol Labelled {
var label: String { get }
}
struct HelloView: View {
let label: String
var body: some View {
Text(label)
}
}
struct ListView<Element: Labelled & Identifiable>: View {
@Binding var elements: [Element]
var body: some View {
List {
ForEach($elements, id: \.id) { $element in
HelloView(label: element.label) // crash
// Replacing the above with a predefined view works correctly
// Text(element.label)
}
}
}
}
struct ForEachBindingRepro: View {
@State var elements: [MyItem] = [
MyItem("hello"),
MyItem("world"),
]
var body: some View {
ListView(elements: $elements)
}
}
#Preview("ForEachBindingRepro") {
ForEachBindingRepro()
}
foreachbindingrepro-2025-07-12-020628.ips
On iPhone, I would like to have a more button at the top right of the navigation bar, a search field in the bottom toolbar, and a plus button to the right of the search field. I've achieved this via the code below.
But on iPad they should be in the navigation bar at the trailing edge from left to right: plus, more, search field. Just like the Shortcuts app, if there's not enough horizontal space, the search field should collapse into a button, and with even smaller space the search bar should become full-width under the navigation bar.
Right now on iPad the search bar is full width under the navigation bar, more at top right, plus at bottom middle, no matter how big the window is.
How can I achieve that? Any way to specify them for the system to more automatically do the right thing, or would I need to check specifically for iPhone vs iPad UIDevice to change the code?
struct ContentView: View {
@State private var searchText = ""
var body: some View {
NavigationStack {
VStack {
Text("Hello, world!")
}
.navigationTitle("Test App")
.searchable(text: $searchText)
.toolbar {
ToolbarItem {
Menu {
//...
} label: {
Label("More", systemImage: "ellipsis")
}
}
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarSpacer(.fixed, placement: .bottomBar)
ToolbarItem(placement: .bottomBar) {
Button {
print("Add tapped")
} label: {
Label("Add", systemImage: "plus")
}
}
}
}
}
}
I present a view in a sheet that consists of a navigation stack and a scroll view which has a photo pushed to the top by setting .ignoresSafeArea(edges: .top). The problem is the top of the photo is blurry due to the scroll edge effect. I would like to hide the scroll edge effect so the photo is fully visible when scrolled to the top but let the effect become visible upon scrolling down. Is that possible?
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
VStack {
Button("Present Sheet") {
showingSheet = true
}
}
.sheet(isPresented: $showingSheet) {
SheetView()
}
}
}
struct SheetView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
ScrollView {
VStack {
Image("Photo")
.resizable()
.scaledToFill()
}
}
.ignoresSafeArea(edges: .top)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(role: .close) {
dismiss()
}
}
ToolbarItem {
EditButton()
}
}
}
}
}
When compiled on Xcode 16.4.0:
When compiled on Xcode 26:
The code:
import SwiftUI
struct SearchBarController: UIViewRepresentable {
@Binding var text: String
var placeholderText: String
class Coordinator: NSObject, UISearchBarDelegate {
@Binding var text: String
init(text: Binding<String>) {
_text = text
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
text = searchText
}
}
func makeUIView(context: Context) -> UISearchBar {
let searchBar = UISearchBar(frame: .zero)
searchBar.delegate = context.coordinator
searchBar.placeholder = placeholderText
searchBar.searchBarStyle = .minimal
return searchBar
}
func updateUIView(_ uiView: UISearchBar, context: Context) {
uiView.text = text
}
func makeCoordinator() -> SearchBarController.Coordinator {
return Coordinator(text: $text)
}
}
We have used searchable modifier with automatic or toolbar placement. When user tap on keyboard's search button it doesn't trigger onSubmit modifier.
However if placement is navigationBarDrawer it is working fine.
.searchable(text: $searchText, placement: .automatic, prompt: "Search")
.onSubmit(of: .search) {
print("Search submitted")
}