Pseudo-code:
PhaseAnimator([false,true], trigger: foo) { flash in
ZStack {
Capsule()
.foregroundStyle(flash ? .red : .green)
Canvas { context, size in
context.draw(image: Image(name: "foo"), toFitRect: some_rectangle);
context.draw(text: Text("foo"), toFitRect: another_rectangle);
}
.foregroundStyle(flash ? .black : .white)
}
} animation: { flash in
return .linear(duration: 0.5);
}
The Capsule's colour animates, but the Canvas's doesn't. The Canvas drawing code is only ever called with flash==false.
What do I have to do to the Canvas so that it redraws with the intermediate colours during the animation?
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.
Post
Replies
Boosts
Views
Activity
Xcode 16.2
Swift 6
macOS Sequoia 15.1
SwiftUI
I am a beginner.
If I understand we can add buttons to the default Menu Bar but not delete them. Am I right? If you do not need most of the buttons, how do you solve that?
I can add a button:
import SwiftUI
@main
struct NouMenuProvesApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
CommandMenu("Custom") {
Button("Custom Action") {
print("Custom Action performed")
}
}
}
}
}
Can I delete a Menu Button or create a new simpler Menu Bar?
Hello,
we are presenting a UIDocumentInteractionController within our app, so the user can share some documents. Sharing basically works but we are facing the problem that the two delegate methods
documentInteractionController(UIDocumentInteractionController, willBeginSendingToApplication: String?)
and
documentInteractionController(UIDocumentInteractionController, didEndSendingToApplication: String?)
are never being called. Other delegate methods such as
documentInteractionControllerWillBeginPreview(UIDocumentInteractionController)
are called just fine. Everything worked as expected when we last checked a year ago or so, but doesn't anymore now, even after updating to the latest iOS 18.3.
Does anybody know of a solution for this?
For reference, this is the simplified code we are using the reproduce the issue:
import UIKit
import OSLog
class ViewController: UIViewController, UIDocumentInteractionControllerDelegate {
let log = Logger(subsystem: "com.me.pdfshare", category: "app")
var documentInteractionController: UIDocumentInteractionController!
override func viewDidLoad() {
super.viewDidLoad()
guard let pdfURL = Bundle.main.url(forResource: "test", withExtension: "pdf") else {
return
}
documentInteractionController = UIDocumentInteractionController(url: pdfURL)
documentInteractionController.delegate = self
documentInteractionController.presentPreview(animated: true)
}
// MARK: - UIDocumentInteractionControllerDelegate
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
log.notice("documentInteractionControllerViewControllerForPreview")
return self
} // This will be called.
func documentInteractionController(_ controller: UIDocumentInteractionController, willBeginSendingToApplication application: String?) {
log.notice("willBeginSendingToApplication")
} // This will NOT be called.
func documentInteractionController(_ controller: UIDocumentInteractionController, didEndSendingToApplication application: String?) {
log.notice("didEndSendingToApplication")
} // This will NOT be called.
}
When building in Xcode 15.4 debug, only a part of the initial View for List is initialized. As you scroll, new ones are initialized, and old ones are destroyed.
When building the same code in Xcode 16.2, ALL Views are initialized first, and then immediately destroyed as you scroll, new ones are initialized, and old ones are destroyed.
MRE:
struct ContentView: View {
private let arr = Array(1...5555)
var body: some View {
List(arr, id: \.self) {
ListCellView(number: $0)
}
}
}
struct ListCellView: View {
private let number: Int
private let arr: [Int]
@StateObject private var vm: ListCellViewModel // Just to print init/deinit
init(number: Int) {
print(#function, Self.self, number)
self.arr = Array(0...number)
self.number = number
let vm = ListCellViewModel(number: number) // To see duplicates of init
self._vm = StateObject(wrappedValue: vm) // because here is @autoclosure
}
var body: some View {
let _ = print(#function, Self.self, number)
Text("\(number)")
}
}
class ListCellViewModel: ObservableObject {
private let number: Int
init(number: Int) {
print(#function, Self.self, number)
self.number = number
}
deinit {
print(#function, Self.self, number)
}
}
An example from a memory report with this code:
Fortunately, the behavior in release mode is the same as in Xcode 15.4. However, the double initialization of Views is still present and has not been fixed.
...
init(number:) ListCellView 42
init(number:) ListCellViewModel 42
init(number:) ListCellView 42
init(number:) ListCellViewModel 42
deinit ListCellViewModel 42
body ListCellView 42
Yes, unnecessary View initializations and extra body calls are "normal" for SwiftUI, but why do they here twice so that one is discarded immediately?
One of my clients is interested in developing a system similar to BrowserStack for internal team usage. Could you please guide me on how to approach the development of this system?
Specifically, the project requires:
Full iPhone screen recording.
Capturing and executing click events on the iPhone.
Do I need to obtain permission from Apple for these functionalities?
I have discovered an odd issue with NSTextView, NSAdaptiveImageGlyph, and NSLayoutManager.
(Forgive the Objective-C... I'm old-school.)
I can easily display an attributed string containing text and Genmoji NSAdaptiveImageGlyphs with something very basic like this:
textView = [[NSTextView alloc] initWithFrame:NSMakeRect(100.0, 100.0, 500.0, 100.0)];
[textView.textStorage setAttributedString:sampleText];
[self addSubview:textView];
//NSLayoutManager *layoutManager = textView.layoutManager;
I can even insert or paste new Genmoji into there as well.
However, if I uncomment out that last line to retrieve the layoutManager of the text view it breaks the rendering of Genmoji, even if I do nothing with the layoutManager! Just getting it causes the NSTextView to skip past Genmoji glyphs when spacing out glyphs and when rendering.
I thought perhaps getting the layoutManager caused an internal cache to break so I tried ensureLayoutForTextContainer but that didn't help.
Interestingly if I paste a new Genmoji into the NSTextView it doesn't display either. Yet if I select all, copy, and paste into TextEdit I see all the Genmoji just fine so they are there, just invisible and zero size. Using the arrow keys I have to skip past the invisible Genmoji.
But if I comment out that line again I see all Genmoji with no issues.
I do need to use a layoutManager to measure things like text bounds and heights. Elsewhere in the code, my existing drawing routines would like to draw the resulting attributed string not with drawAtPoint (which works fine) but with drawGlyphsForGlyphRange but that doesn't work either as it uses NSLayoutManager.
Is there a trick to getting NSAdaptiveImageGlyphs working with NSLayoutManager?
Thanks for any assistance!
We are trying to implement live caller id lookup, there is lack of documentation and we would like to understand few blockers that we have:
Based on this documentation page: https://developer.apple.com/documentation/identitylookup/setting-up-the-http-endpoints-for-live-caller-id-lookup:
What exactly should accept/return these endpoints?. There is no detailed description of the response/request schema for any endpoint.
What are the usage scenarios of the "/key" method? If it is possible to get a more detailed explanation regarding Evaluation Key usage as it is not clear from the available documentation.
Based on this step by step implementation description: https://github.com/apple/live-caller-id-lookup-example/blob/main/Sources/PIRService/PIRService.docc/Authentication.md:
The 6th step: "Authentication server returns the list of public keys (potentially through a proxy)". What is the endpoint path and response format expected by the iOS system? This step refers to calling the '/config' endpoint ?
The 8th step: "Authentication server verifies the User Token and returns the public key that is associated with the User Tier. ..." What is the the endpoint path and request/response format expected by the iOS system?
The 9th step: "The system constructs a Privacy Pass token request using the specific public key. The token request is sent along with the User Token to the authentication server". What is the request/response format expected by the iOS system?
The 11th step: "When a PIR request is made, the system attached an unused Privacy Pass token to the request. The PIR node can use the public key to verify that the token is valid and that assures that the request is authorized". What is the request/response format expected by the iOS system?
On executing refreshPIRParameters fom app I get this error:
LiveCallerIDLookupManager.shared.refreshPIRParameters(forExtensionWithIdentifier: LiveCallerIdExtensionName)
Unable to query status due to errors: server error (Access DeniedAccess DeniedYou don't have permission to access "http://www.example.com/config" on this server.Reference #18.cdde00d4.1738074526.3a38870https://errors.edgesuite.net/18.cdde00d4.1738074526.3a38870)
Why it tries to reach example.com/config but not our serviceURL or tokenIssuerURL?
Any help would be appreciated.
I tried to use AppIntentConfiguration in iOS17 to fail to achieve such a dynamic configuration.
code:
struct ConfigurationAppIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource { "位置" }
static var description: IntentDescription { "选择位置以展示城市天气" }
@Parameter(title: "Select City", optionsProvider: CityOptionsProvider())
var selectedCity: String?
}
We are using the contactAccessPicker modifier connected to a Button to allow the user to change the selection of contacts that he allows for use in our app. In the two places where the iOS UI screen refers to our app:
"manage which contacts can access." on top,
and below in the explanatory text, again ,
the value of is taken probably from the app's PRODUCT_NAME. Instead, we need it to be either CFBundleName or CFDisplayBundleName.
In our case they are different (PRODUCT_NAME is legacy, reasons of rebranding, which is a very common reason in apps).
Is there a specific reason why iOS is using PRODUCT_NAME (or something similar) in the contactAccessPicker UI screen instead of the user facing CFBundleName or CFDisplayBundleName? or is this a bug?
I use
for (NSNumber *controlState in AllControlStates()) {
[uiButton setAttributedTitle:attributedStrings[controlState] forState:controlState.unsignedIntegerValue];
}
to set different title colors based on different controlState now.
How to use UIButtonConfiguration to set attributedTitle based on control state?
I am deprecating contentEdgeInsets for my button now, I have to set all the other properties on configuration to maintain the current look of my button.
I'm using UIDocumentBrowserViewController. This view controller automatically creates a TabView with navigation titles and up to two trailing navigation bar items.
To visualize this, open the Files app by Apple on an iPhone.
I want to do the following:
Add a third button and place it farthest on the trailing side.
Keep all three buttons blue (the default color), but adjust the color of the navigation title to use the primary text color (it is also currently blue, by default)
Button Order
If my button is represented by C, then the order from left-to-right or leading-to-trailing should be A B C.
I tried to add it by using additionaltrailingnavigationbarbuttonitems:
class DocumentBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
let button = UIBarButtonItem(...)
additionalTrailingNavigationBarButtonItems.append(button)
}
}
This always adds it as the leftmost trailing item. The order when the view loads is C A B, where C represents my button.
Here are some things I've tried:
Add it in viewWillAppear - same results.
Add it in viewDidAppear - same results.
Add it using rightBarButtonItems - does not show up at all.
insert it at: 0 instead of appending it - same results.
Add it with a delay using DispatchQueue.main.async - same results.
After some experimentation, I realized that the arrays referenced by additionalTrailingNavigationBarButtons and rightBarButtonItems seem to be empty, other than my own button. This is the case even if the DispatchQueue delay is so long that the view has already rendered and the two default buttons are clearly visible. So I'm not sure how to place my button relative to these, since I can't figure out where they actually are in the view controller's properties.
How do I put my button farther to the trailing/right side of these two default buttons?
Title Color
The navigation titles created by UIDocumentBrowserViewController are blue when not in their inline format. I want them to use the primary text color instead.
In viewDidLoad, I could do something like this:
UINavigationBar.appearance().tintColor = UIColor.label
This will change the title color to white or black, but it will also change the color of the buttons. I've tried various approaches like titleTextAttributes, and none of them seem to work with this view controller.
How do I change just the color of the navigation title, and not the color of the navigation bar items?
If I use scrollTo on its own, it works fine (actually not fine, it often scrolls to the wrong place with LazyVStack, but thats another topic)
However, if I also want to use onScrollVisibilityChange on one of my items in my ForEach and if I do, it just stops working. Everything still fires, but nothing scrolls.
This is on the latest macOS, and iOS.
WHY? How can I fix this?
You can find more details at https://stackoverflow.com/questions/79391208/swiftui-scrollto-function-stops-working-if-i-also-use-onscrollvisibilitychange
Its important to note that this same app did not have this issue in iOS 17.
Ever since iOS 18 I have noticed that when application written in SwiftUI uses Label with the default color (which auto changes between light and dark appearance), from time to time when resuming an application that has been in the background, the color of those labels (only the Label elements) switches from the opposite to the correct one. Here is an example:
Steps to reproduce
Phone is in dark appearance
Open app
Labels text color is white and labels background is black
Go to home so that app is on background
Wait random time (does not happen all the time), some times 1 min some times 10
Reopen the application.
During the opening transition the Label text color was changed while the app was in suspended mode to the light appearance variant (black)
Once the app opening transition finishes the Label text color switches back to the correct color for dark appearance (white)
Same issue happens if you start from light appearance. I cannot reproduce this on Xcode simulators, I have tried to force memory warning to check if that has anything to do with it but that also does not cause the issue to appear on simulators. For now I can only reproduce this on real device.
Screenshots
Here is screenshots of the above example:
During transition
After transition
We are developing an MacOS app from our iOS app using MacCatalyst.
If I press long on the app icon on the Dock, a list of recent files appears.
If I tap one one of these files nothing happens. I would expect the scene delegate function:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)
would be called but it is not. Can somebody maybe explain what I am missing here?
The list of recent files also appears in the Menu under File > Open recent files. There I can tap on a file and it it is opened correctly using the scene delegate method mentioned above.
The files can also be opened with the app using the Finder, so the associated file types with the app are correct.
Hi,
After running the Xcode template "New project > (SwiftUI, SwiftData)" I noticed that there is no insert animation into the list.
The project is a pure vanilla project created from Xcode (Version 16.2 (16C5032a)) and the code is a simple as it gets:
//
// ContentView.swift
//
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
var body: some View {
NavigationSplitView {
List {
ForEach(items) { item in
NavigationLink {
Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
} label: {
Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
}
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
} detail: {
Text("Select an item")
}
}
private func addItem() {
withAnimation {
let newItem = Item(timestamp: Date())
modelContext.insert(newItem)
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
}
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}
As you see the template's code does have withAnimation inside addItem but there is no animation. The new added item appears without animation in the the List
Here is a short video. Is this a known bug?
0
I am developing a custom Picture in Picture (PiP) app that plays videos. The video continues to play even when the app goes to the background, but I would like to be able to get the bool value when the PiP is hidden on the edge, as shown in the attached image. The reason why we need this is because we don't want the user to consume extra network bandwidth, so we want to pause the video when the PiP is hidden on the edge of the screen. Please let me know if this is possible. This can be done in the foreground or in the background.
Hi all!
Based on documentation: https://developer.apple.com/documentation/carplay/cplistitem/handler
If you need to perform asynchronous tasks, dispatch them to a background queue and call the completion closure or completionBlock when they complete.
In a normal case, it works perfectly. But, if it takes "too much", for example, 10 seconds (streaming with retries, app business logic), when I call the "completionBlock" inside "handler" doesn't do anything.
Exists a timeout in "completionBlock"?
Thanks!
I am developing a custom Picture in Picture (PiP) app that plays videos.
The video continues to play even when the app goes to the background, but I would like to be able to get the bool value when the PiP is hidden on the edge, as shown in the attached image.
The reason why we need this is because we don't want the user to consume extra network bandwidth, so we want to pause the video when the PiP is hidden on the edge of the screen.
Please let me know if this is possible.
This can be done in the foreground or in the background.
I’m experiencing significant performance and memory management issues in my SwiftUI application when displaying a large number of images using LazyVStack within a ScrollView. The application uses Swift Data to manage and display images.
Here’s the model I’m working with:
@Model
final class Item {
var id: UUID = UUID()
var timestamp: Date = Date.now
var photo: Data = Data()
init(photo: Data = Data(), timestamp: Date = Date.now) {
self.photo = photo
self.timestamp = timestamp
}
}
extension Item: Identifiable {}
The photo property is used to store images. However, when querying Item objects using Swift Data in a SwiftUI ScrollView, the app crashes if there are more than 100 images in the database.
Scrolling down through the LazyVStack loads all images into memory leading to the app crashing when memory usage exceeds the device’s limits.
Here’s my view: A LazyVStack inside a ScrollView displays the images.
struct LazyScrollView: View {
@Environment(\.modelContext) private var modelContext
@State private var isShowingPhotosPicker: Bool = false
@State private var selectedItems: [PhotosPickerItem] = []
@Query private var items: [Item]
var body: some View {
NavigationStack {
ScrollView {
LazyVStack {
ForEach(items) { item in
NavigationLink {
Image(uiImage: UIImage(data: item.photo)!)
.resizable()
.scaledToFit()
} label: {
Image(uiImage: UIImage(data: item.photo)!)
.resizable()
.scaledToFit()
}
}
}
}
.navigationTitle("LazyScrollView")
.photosPicker(isPresented: $isShowingPhotosPicker, selection: $selectedItems, maxSelectionCount: 100, matching: .images)
.onChange(of: selectedItems) {
Task {
for item in selectedItems {
if let data = try await item.loadTransferable(type: Data.self) {
let newItem = Item(photo: data)
modelContext.insert(newItem)
}
}
try? modelContext.save()
selectedItems = []
}
}
}
}
}
Based on this:
How can I prevent SwiftUI from loading all the binary data (photo) into memory when the whole view is scrolled until the last item?
Why does SwiftUI not free memory from the images that are not being displayed?
Any insights or suggestions would be greatly appreciated. Thank you!
I will put the full view code in the comments so anyone can test if needed.
According to the MVVM design pattern, one of my views depends on many properties in my model. Can I use logic like @published var model = MyModel()? Will there be a large performance loss? Will the UI be refreshed when other unrelated properties in the model are modified? What is the best practice in this case?