I'm upgrading my app from minVersion iOS 11 to iOS 12. My compiler says that UIDocumentMenuViewController with UIDocumentPickerViewController is deprecated, they recommend to use directly the last one. So I change the code.
fileprivate func openDocumentPicker() {
let documentPicker = UIDocumentPickerViewController(
documentTypes: [
"com.adobe.pdf",
"org.openxmlformats.wordprocessingml.document", // DOCX
"com.microsoft.word.doc" // DOC
],
in: .import
)
documentPicker.delegate = self
view.window?.rootViewController?.present(documentPicker, animated: true, completion: nil)
}
When I open the picker in iOS 17.2 simulator and under it is well shown, like a page sheet. But in iOS 18.0 and up at first it opens like a page sheet with no content but then it is displayed as a transparent window with no content. Is there any issue with this component and iOS 18? If I open the picker through UIDocumentMenuViewControllerDelegate in an iphone with iOS 18.2 it is well shown.
Image in iOS 18.2 with the snippet
The same snippet in iOS 17.2 (and expected in older ones)
Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hello!
I’m experiencing a crash in my iOS/iPadOS app related to a CALayer rendering process. The crash occurs when attempting to render a UIImage on a background thread. The crashes are occurring in our production app, and while we can monitor them through Crashlytics, we are unable to reproduce the issue in our development environment.
Relevant Code
I have a custom view controller that handles rendering CALayers onto images. This method creates a CALayer on the main thread and then starts a detached task to render this CALayer into a UIImage. The whole idea is learnt from this StackOverflow post: https://stackoverflow.com/a/77834613/9202699
Here are key parts of my implementation:
class MyViewController: UIViewController {
@MainActor
func renderToUIImage(size: CGSize, itemsToDraw: [MyDrawingItem], transform: CGAffineTransform) async -> UIImage? {
// Create CALayer and add it to the view.
CATransaction.begin()
let customLayer = MyDrawingLayer()
customLayer.setupContent(itemsToDraw: itemsToDraw)
// Position the frame off-screen to it hidden.
customLayer.frame = CGRect(
origin: CGPoint(x: -100 - size.width, y: -100 - size.height),
size: size)
customLayer.masksToBounds = true
customLayer.drawsAsynchronously = true
view.layer.addSublayer(customLayer)
CATransaction.commit()
// Render CALayer to UIImage in background thread.
let image = await Task.detached {
customLayer.setNeedsDisplay()
let renderer = UIGraphicsImageRenderer(size: size)
let image = renderer.image { // CRASH happens on this line
let cgContext = $0.cgContext
cgContext.saveGState()
cgContext.concatenate(transform)
customLayer.render(in: cgContext)
cgContext.restoreGState()
}
return image
}.value
// Remove the CALayer from the view.
CATransaction.begin()
customLayer.removeFromSuperlayer()
CATransaction.commit()
return image
}
}
class MyDrawingLayer: CALayer {
var itemsToDraw: [MyDrawingItem] = []
func setupContent(itemsToDraw: [MyDrawingItem]) {
self.itemsToDraw = itemsToDraw
}
override func draw(in ctx: CGContext) {
for item in itemsToDraw {
// Render the item to the context (example pseudo-code).
// All items are thread-safe to use.
// Things to draw may include CGPath, CGImages, UIImages, NSAttributedString, etc.
item.draw(in: ctx)
}
}
}
Crash Log
The crash occurs at the following location:
Crashed: com.apple.root.default-qos.cooperative
0 MyApp 0x5cb300 closure #1 in closure #1 in MyViewController.renderToUIImage(size: CGSize, itemsToDraw: [MyDrawingItem], transform: CGAffineTransform) + 4313002752 (<compiler-generated>:4313002752)
1 MyApp 0x5cb300 closure #1 in closure #1 in MyViewController.renderToUIImage(size: CGSize, itemsToDraw: [MyDrawingItem], transform: CGAffineTransform) + 4313002752 (<compiler-generated>:4313002752)
2 MyApp 0x1a4578 AnyModifier.modified(for:) + 4308649336 (<compiler-generated>:4308649336)
3 MyApp 0x7b4e64 thunk for @escaping @callee_guaranteed (@guaranteed UIGraphicsPDFRendererContext) -> () + 4315008612 (<compiler-generated>:4315008612)
4 UIKitCore 0x1489c0 -[UIGraphicsRenderer runDrawingActions:completionActions:format:error:] + 324
5 UIKitCore 0x14884c -[UIGraphicsRenderer runDrawingActions:completionActions:error:] + 92
6 UIKitCore 0x148778 -[UIGraphicsImageRenderer imageWithActions:] + 184
7 MyApp 0x5cb1c0 closure #1 in MyViewController.renderToUIImage(size: CGSize, itemsToDraw: [MyDrawingItem], transform: CGAffineTransform) + 100 (FileName.swift:100)
8 libswift_Concurrency.dylib 0x60f5c swift::runJobInEstablishedExecutorContext(swift::Job*) + 252
9 libswift_Concurrency.dylib 0x62514 swift_job_runImpl(swift::Job*, swift::SerialExecutorRef) + 144
10 libdispatch.dylib 0x15ec0 _dispatch_root_queue_drain + 392
11 libdispatch.dylib 0x166c4 _dispatch_worker_thread2 + 156
12 libsystem_pthread.dylib 0x3644 _pthread_wqthread + 228
13 libsystem_pthread.dylib 0x1474 start_wqthread + 8
Questions
Is it safe to run UIGraphicsImageRenderer.image on the background thread?
Given that I want to leverage GPU rendering, what are some best practices for rendering images off the main thread while ensuring stability?
Are there alternatives to using UIGraphicsImageRenderer for background rendering that can still take advantage of GPU rendering?
It is particularly interesting that the crash logs indicate the error may be related to UIGraphicsPDFRendererContext (crash log line number 3). It would be very helpful if someone could explain the connection between starting and drawing on a UIGraphicsImageRenderer and UIGraphicsPDFRendererContext.
Any insights or guidance on this issue would be greatly appreciated. Thanks!!!
Working on a MacOS SwiftUI app - recently, when the app stays running from the night before, it no longer responds to keystrokes - data entry, or cursor navigation keys.
It does respond to mouse activity and clicks ... very weird.
Hey, I am developing my app in Swift using Xcode for iOS 16 or later.
I want to implement the navigation behavior found in apps like WhatsApp or Instagram, where navigating from the feed to a user's profile keeps the tab bar visible. Then, when opening a chat from the profile, a new view appears, leaving the profile view behind along with the tab bar.
I have this code as a base to achieve this, but when navigating from View 1 to View 2, it applies the effect that should only happen in View 3. I haven't been able to prevent View 2 from using this effect and limit it only to View 3.
Can anyone help me???
import SwiftUI
struct ContentView: View {
@State private var path: [String] = [] // Controls the navigation stack in View1
var body: some View {
NavigationStack(path: $path) {
TabView {
View1(path: $path)
.tabItem {
Label("View 1", systemImage: "1.circle")
}
View4()
.tabItem {
Label("View 4", systemImage: "4.circle")
}
}
.navigationDestination(for: String.self) { value in
if value == "View3" {
View3(path: $path) // View3 outside the TabView
}
}
}
}
}
struct View1: View {
@Binding var path: [String]
var body: some View {
VStack {
Text("View 1 with TabBar")
Button("Go to View 2") {
path.append("View2") // Adds View2 to the stack
}
}
.navigationDestination(for: String.self) { value in
if value == "View2" {
View2(path: $path)
}
}
}
}
struct View2: View {
@Binding var path: [String]
var body: some View {
VStack {
Text("View 2 with TabBar")
Button("Go to View 3 (Without TabBar)") {
path.append("View3") // Adds View3 to the stack
}
}
}
}
struct View3: View {
@Binding var path: [String]
var body: some View {
VStack {
Text("View 3 without TabBar")
.font(.largeTitle)
.padding()
Button("Go Back") {
path.removeLast() // Returns to View2
}
}
}
}
struct View4: View {
var body: some View {
Text("View 4 with TabBar")
}
}
How to share 'back facing' iOS camera app at same time Eye Tracking app needs 'front facing' camera?
While using my xmas present of a new iPhone and iOS 18.2, I figured I'd try the Eye Tracker app. I've been working with clients successfully using Tobii and other existing eye trackers. In my limited tests, Apple has room for improvement.
My main issue is with the camera app which cannot be used at the same time while using the Eye Tracker app. I get an error popup from Apple:
Camera is use by another app
The image below is from my app showing the popup message "Camera in use by another app", but the same error occurs on the installed camera app. This error is from Apple, not my app.
For terminology: 'front' camera is the one pointing at the user (the selfi camera) while 'back' camera is the main one with multiple lenses. Eye tracking needs the 'front' camera.
It seems when an app uses the camera, it takes over both the front and back facing cameras (since you might swap them). Thus another app, especially Eye Tracking, cannot use just the front facing camera at the same time.
That limits use of Eye Tracking, in particular one cannot take pictures or click any buttons on an app that uses the camera.
Anyone know of a way for an app to not take over both front and back cameras at the same time? If I can separate them, the Eye Tracker could use the front camera while the camera uses the back camera.
Is it the default behavior that the standard back swipe (interactivePopGestureRecognizer) does not work when running a designed for iPhone app on an iPad?
To my knowledge, all apps behave this way.
Are there any workarounds?
Starting from Sequoia release 15.2 apps crash with following call stack, when adding static text controls. First call to [NSTextField setStringValue] causes following crash
0 libobjc.A.dylib 0x19f2f5820 objc_msgSend + 32
1 AppKit 0x1a3355460 -[NSCell _objectValue:forString:errorDescription:] + 144
2 AppKit 0x1a3355348 -[NSCell setStringValue:] + 48
3 AppKit 0x1a33af9fc -[NSControl setStringValue:] + 104
4 AppKit 0x1a3d1f190 -[NSTextField setStringValue:] + 52
It happens on specific MacBook Pro models(16 in MacBook Pro).
Crash analysis found that isa pointer of the object was corrupted for the object NSCell. Enabled Zombies in debugging, not found any issue. Also tried address sanitizer. Since the issue started with a recent release of macOS, any changes in the Appkit in the recent releases trigger the crash? Any help/suggestions would be appreciated.
Topic:
UI Frameworks
SubTopic:
AppKit
Hello!
I have a destination navigation which is TabVIew where each tab item is ScrollView. And when scrolling content of any of tab items is underneath navigation bar its background is always hidden. But at the same time tab bar background is toggled depending on scrolling content position.
I expected it would work with TabView the same as with any other view.
Is it supposed to work like that?
How do I draw a single line of text in a SwiftUI Canvas, scaled to fill a given rectangle?
Example:
Canvas { context, size in
let r = CGRect(origin: CGPointZero, size: size); // Whole canvas
let t = Text("Hello World");
context.draw(t, in: r);
}
Outside of Canvas I'd add .minimumScaleFactor(0) .lineLimit(1), and I guess set a large default font size, and I'd get the result I want.
But inside Canvas, .minimumScaleFactor and .lineLimit don't seem to be available; they return some View, not Text, which can't be used in context.draw. (Is there a trick to make that work?)
I have written the following to do this, but I think there must be an easier way to achieve this! Suggestions?
extension GraphicsContext {
mutating func draw_text_in_rect(string: String, rect: CGRect)
{
let text = Text(string) .font(.system(size: 25));
// The font size used here does matter, because e.g. letter spacing
// varies with the font size.
let resolved = resolve(text);
let text_size = resolved.measure(in: CGSize(width: CGFloat.infinity, height: CGFloat.infinity));
let text_aspect = text_size.width / text_size.height;
let fit_size = CGSize(width: min(rect.size.width, rect.size.height*text_aspect),
height: min(rect.size.height, rect.size.width/text_aspect));
let fit_rect = CGRect(x: rect.origin.x + (rect.size.width-fit_size.width)/2,
y: rect.origin.y + (rect.size.height-fit_size.height)/2,
width: fit_size.width,
height: fit_size.height);
let scale = fit_size.width / text_size.width;
// For debug:
// var p = Path();
// p.addRect(fit_rect);
// stroke(p, with: GraphicsContext.Shading.color(.red), lineWidth: 1);
translateBy(x: fit_rect.minX, y: fit_rect.minY);
scaleBy(x:scale, y:scale);
draw(resolved, at: CGPointZero, anchor: UnitPoint.topLeading);
transform = CGAffineTransformIdentity;
}
};
I'm creating an app which gamifies Screen Time reduction. I'm running into an issue with apples Screen Time setting where the user can disable my apps "Screen Time access" and get around losing the game.
Is there a way to detect when this setting is disabled for my app? I've tried using AuthorizationCenter.shared.authorizationStatus but this didn't do the trick. Does anyone have an ideas?
As per the documentation link, the Tab initializer in SwiftUI should allow supplying a custom view to the Label. However, the colors used within the Label view are not being honored as expected.
I attempted to set custom colors in the Label, but they either default to system-defined styles or are ignored entirely. This behavior does not align with my understanding of how custom views should work in SwiftUI's Label.
Am I missing a step or configuration here, or is this a bug in the current implementation?
struct ContentView: View {
@State private var activeTab: TabItem = .homeTab
var body: some View {
TabView(selection: $activeTab) {
ForEach(TabItem.allCases) { tabItem in
Tab(value: tabItem) {
getView(for: tabItem)
} label: {
VStack(spacing: 0) {
MainTabButtonView(
selected: activeTab == tabItem,
tabItem: tabItem
)
Text(tabItem.title)
}
}
}
}
}
}
private extension ContentView {
@ViewBuilder
func getView(for tabItem: TabItem) -> some View {
switch tabItem {
case .homeTab:
Text("Home")
case .searchTab:
Text("Search")
case .profileTab:
Text("Profile")
case .moreTab:
Text("More")
}
}
}
#Preview {
ContentView()
}
enum TabItem: String, Identifiable, Hashable, CaseIterable {
case homeTab
case searchTab
case profileTab
case moreTab
var tabImage: String {
switch self {
case .homeTab:
"house"
case .searchTab:
"magnifying-glass"
case .profileTab:
"biographic"
case .moreTab:
"hamburger-menu"
}
}
var title: String {
switch self {
case .homeTab:
"Home"
case .searchTab:
"Search"
case .profileTab:
"Profile"
case .moreTab:
"More"
}
}
var id: String {
rawValue
}
}
struct MainTabButtonView: View {
private let selected: Bool
private let tabItem: TabItem
init(
selected: Bool,
tabItem: TabItem
) {
self.selected = selected
self.tabItem = tabItem
}
var body: some View {
Image(tabItem.tabImage)
.renderingMode(.template)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 30, height: 30)
.foregroundStyle(
selected ? Color.green : Color.orange
)
}
}
Expected Behavior:
The custom colors applied within the Label should render as defined.
Actual Behavior:
The colors are overridden or ignored, defaulting to the system-defined styles.
Environment:
Xcode Version: Xcode 16.2
iOS: 18.2
Swift Version: Swift 6
Hello, since I'm using SwiftUI and some interface should be optimized for different devices(like iPad and iPhone), so I'm curious that what device would be used to run the App? Is it iPad or iPhone?
And could I use SDKs like SiriKit?
Starting with iOS 18, the behavior of searchable and searchSuggestions differs from previous versions.
In iOS 17.5, searchSuggestions remained visible even after selecting an item and navigating away. However, in iOS 18, searchSuggestions are dismissed after navigation.
Is there a way to keep searchSuggestions visible after navigation, as in iOS 17.5?
struct ContentView: View {
@State private var query = ""
var body: some View {
NavigationStack {
Color.red
.searchable(text: $query)
.searchSuggestions {
NavigationLink("Element") {
Color.blue
}
}
}
}
}
iOS 18.1
iOS 17.5
I'm testing using Group Activities and having no trouble iOS<->iOS or starting an activity on macOS and joining via iOS. However, when I start an activity and then try to join it from another macOS client, the starting side joins the session just fine, but the receiving side acts like I don't have the required app, even when it is already running.
I see the active SharePlay icon in the menu bar, and the Current Activity is shown, but instead of an "Open" button there is a "MyApp Required" string and a "View" button that goes to the App Store. (Where the app is not available yet, as expected, since I'm still working on it.) There is no GroupSession started on that Mac yet, obviously.
I'm looking for any hints to help debug what is going on. How does Group Activities find the app for the activity on macOS and how can I figure out why it isn't finding mine?
Thanks!
More specifically, if I have 2 pinned tabs that are displayed in different order for compact and regular and switch between compact and regular, when it gets to regular mode, the images for both tabs will not be rendered. The tabs can still be pressed, just the icon is missing.
As you can see in the screenshot, the verification popups that appear when making a StoreKit purchase cut off the buttons. When typing the code into the input field, the window will also flicker and stutter with random view refreshes. Is this something I can configure/change? It's not a very pleasant experience for making an in app purchase.
I want to have my own background and foreground colors for some views and I am having a bit of trouble. I cannot figure out how to remove the margins around some built-in views. One example is below. The ScrollView portion is always black or white, depending on whether I am I dark mode or not. I've added various colors and borders to see what is going on below. I've also tried adding the modifiers to the Scroll View rather than the TextEditor and it doesn't work at all. If I don't have the .frame modifier on the ScrollView, the TextEditor moves to the top of its frame for some reason. I've played with .contentMargins, .edgeInsets, etc. with no luck
How do I get the TextEditor to fill the entire ScrollView without the margin? Thanks!
import SwiftUI
struct TextEditorView: View
{ @Binding var editString: String
var numberOfLines: Int
var lineHeight: CGFloat
{ UIFont.preferredFont(forTextStyle: .body).lineHeight
}
var viewHeight: CGFloat
{ lineHeight * CGFloat(numberOfLines) + 8
}
var body: some View
{ ScrollView([.vertical], showsIndicators: true)
{ TextEditor(text: $editString)
.border(Color.red, width: 5)
.foregroundStyle(.yellow)
.background(.blue)
.frame(minHeight:viewHeight, maxHeight: viewHeight)
.scrollContentBackground(.hidden)
}
.frame(minHeight:viewHeight, maxHeight: viewHeight)
}
}
Hi,,
How can I make a rectangle become taller as I swipe down on my trackpad?"
struct BlueRectangleView: View {
var body: some View {
VStack {
Rectangle()
.fill(Color.blue)
.frame(width: 200, height: 100)
.cornerRadius(10)
.shadow(radius: 5)
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.white)
}
}
struct BlueRectangleView_Previews: PreviewProvider {
static var previews: some View {
BlueRectangleView()
}
}
I'm pretty new to Swift and SwiftUI. I'm making my first app for sorting a gallery with some extra features.
I was using my own iPhone for testing and just started testing my app on other Apple products.
Everything works fine on iPad Air M1, iPhone 15 Pro, iPhone 15 Pro Max, iPhone 13, iPhone XS (Simulator), and iPhone 11 Pro (Simulator). However, when I tried to show my app to a family member with an iPhone 11, I came across an issue.
Issue Description:
My app takes all photos from iPhone's native gallery, then you can sort it by some spesific filters and delete pictures. It just looks like the native gallery. (I can add photos later if needed) You can just scroll the gallery by swiping up and down. You can press the select button and start selecting pictures to delete.
I recently added a drag-to-select-multiple-pictures feature. This makes it feel more like the native iOS experience, eliminating the need to tap each picture individually.
However, on the iPhone 11, the moment you open the app, you can't scroll. Scrolling is completely locked. You can still select pictures by tapping or dragging, so it's not a touch area issue. The same issue persists on the iPhone 11 simulator.
And I think I found the problematic part in my (sadly messy) ContentView.swift file;
ScrollView {
RefreshControl(coordinateSpace: .named("refresh")) {
await viewModel.refreshMediaItems()
}
LazyVGrid(columns: gridColumns, spacing: UIDevice.current.userInterfaceIdiom == .pad ? 12 : 4) {
let items = viewModel.filteredItems(typeFilter: mediaTypeFilter, specialFilter: specialFilter)
ForEach(Array(zip(items.indices, items)), id: \.1.id) { index, item in
MediaThumbnailView(
item: item,
isSelected: selectedItems.contains(item.id),
viewModel: viewModel,
onLongPress: {
if !isSelectionMode {
toggleSelectionMode()
selectedItems.insert(item.id)
}
},
onTap: {
if isSelectionMode {
toggleSelection(item: item)
} else {
viewModel.selectItem(item)
}
}
)
.aspectRatio(1, contentMode: .fit)
.background(
GeometryReader { geometry in
let frame = geometry.frame(in: .named("grid"))
Color.clear.preference(
key: ItemBoundsPreferenceKey.self,
value: [ItemBounds(id: item.id, bounds: frame, index: index)]
)
}
)
}
}
.padding(.horizontal, 2)
.coordinateSpace(name: "grid")
.onPreferenceChange(ItemBoundsPreferenceKey.self) { bounds in
itemBounds = Dictionary(uniqueKeysWithValues: bounds.map { ($0.id, $0) })
itemIndices = Dictionary(uniqueKeysWithValues: bounds.map { ($0.id, $0.index) })
}
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { gesture in
if isSelectionMode {
let location = gesture.location
if !isDragging {
startDragging(at: location, in: itemBounds)
}
updateSelection(at: location, in: itemBounds)
}
}
.onEnded { _ in
endDragging()
}
)
}
.coordinateSpace(name: "refresh")
}
you can see the .gesture(.... part. I realised that this DragGesture and ScrollView blocks each other (somehow only on iPhone 11)
highPriorityGesture also won't work.
When I change it with simultaneousGesture, scroll starts to work again.
BUT - since it's simultaneous, when multiple selection mode is activated, when I'm dragging my finger gallery also starts to scroll and it becomes a very unpleasant experience. After this issue I realised on native gallery iOS locks scroll when you are dragging for multiple selection and just when you release your finger you can scroll again even if the multiple selection mode is active.
I tried a million things, asked claude, chatgpt etc. etc.
Found some similar issues on stackoverflow but they were all related to iOS 18, not spesific to an iPhone. My app works fine on iOS 18 (15 Pro Max)
iOS 18 drag gesture blocks scrollview
Here are the some of the things I've tried: using highPriorityGesture and simultenousgesture together, tried to lock the scroll briefly while dragging, implement much complicated versions of these things with the help of claude, try to check if isSelectionMode is true or not
All of them broke other things/won't work.
Probably there's something pretty simple that I'm just missing; but iPhone 11 being the single problematic device confuses me. I don't want to mess too much with my already fragile logic.
The SwiftUI Playground code below demonstrates that a .jpeg image can be read and written to the iOS file system. While, a.png image can only be read; the writing request appears to be ignored. Can anyone please tell me how to code to save a .png image using SwiftUI to the iOS file system.
Code:
import SwiftUI
import UniformTypeIdentifiers
/*
(Copied from Playground 'Help' menu popup.)
UIImage Summary
An object that manages image data in your app.
You use image objects to represent image data of all kinds, and the UIImage class is capable of managing data for all image formats supported by the underlying platform. Image objects are immutable, so you always create them from existing image data, such as an image file on disk or programmatically created image data. An image object may contain a single image or a sequence of images for use in an animation.
You can use image objects in several different ways:
Assign an image to a UIImageView object to display the image in your interface.
Use an image to customize system controls such as buttons, sliders, and segmented controls.
Draw an image directly into a view or other graphics context.
Pass an image to other APIs that might require image data.
Although image objects support all platform-native image formats, it’s recommended that you use PNG or JPEG files for most images in your app. Image objects are optimized for reading and displaying both formats, and those formats offer better performance than most other image formats. Because the PNG format is lossless, it’s especially recommended for the images you use in your app’s interface.
Declaration
class UIImage : NSObject
UIImage Class Reference
*/
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ImageFileDoc: FileDocument {
static var readableContentTypes = [UTType.jpeg, UTType.png]
static var writableContentTypes = [UTType.jpeg, UTType.png]
var someUIImage: UIImage = UIImage()
init(initialImage: UIImage = UIImage()) {
self.someUIImage = initialImage
}
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let some = UIImage(data: data)
else {
throw CocoaError(.fileReadCorruptFile)
}
self.someUIImage = some
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
switch configuration.contentType {
case UTType.png:
if let data = self.someUIImage.pngData() {
return .init(regularFileWithContents: data)
}
case UTType.jpeg:
if let data = self.someUIImage.jpegData(compressionQuality: 1.0) {
return .init(regularFileWithContents: data)
}
default:
break
}
throw CocoaError(.fileWriteUnknown)
}
}
struct ContentView: View {
@State private var showingExporterPNG = false
@State private var showingExporterJPG = false
@State private var showingImporter = false
@State var message = "Hello, World!"
@State var document: ImageFileDoc = ImageFileDoc()
@State var documentExtension = ""
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text(message)
Button("export") {
if documentExtension == "png" {
message += ", showingExporterPNG is true."
showingExporterPNG = true
}
if documentExtension == "jpeg" {
message += ", showingExporterJPG is true."
showingExporterJPG = true
}
}
.padding(20)
.border(.white, width: 2.0)
.disabled(documentExtension == "")
Button("import") {
showingImporter = true
}
.padding(20)
.border(.white, width: 2.0)
Image(uiImage: document.someUIImage)
.resizable()
.padding()
.frame(width: 300, height: 300)
}
// exporter .png
.fileExporter(isPresented: $showingExporterPNG, document: document, contentType: UTType.png) { result in
switch result {
case .success(let url):
message += ", .\(documentExtension) Saved to \(url.lastPathComponent)"
case .failure(let error):
message += ", Some error saving file: " + error.localizedDescription
}
}
// exporter .jpeg
.fileExporter(isPresented: $showingExporterJPG, document: document, contentType: UTType.jpeg) { result in
switch result {
case .success(let url):
message += ", .\(documentExtension) Saved to \(url.lastPathComponent)"
case .failure(let error):
message += ", Some error saving file: " + error.localizedDescription
}
}
// importer
.fileImporter(isPresented: $showingImporter, allowedContentTypes: [.png, .jpeg]) { result in
switch result {
case .failure(let error):
message += ", Some error reading file: " + error.localizedDescription
case .success(let url):
let gotAccess = url.startAccessingSecurityScopedResource()
if !gotAccess {
message += ", Unable to Access \(url.lastPathComponent)"
return
}
documentExtension = url.pathExtension
guard let fileContents = try? Data(contentsOf: url)
else {
message += ",\n\nUnable to read file: \(url.lastPathComponent)\n\n"
url.stopAccessingSecurityScopedResource()
return
}
url.stopAccessingSecurityScopedResource()
message += ", Read file: \(url.lastPathComponent)"
message += ", path extension is '\(documentExtension)'."
if let uiImage = UIImage(data: fileContents) {
self.document.someUIImage = uiImage
}else{
message += ", File Content is not an Image."
}
}
}
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
Tags:
Files and Storage
Developer Tools
iPad
Swift Playground