Hello! We can animate Text color via foregroundStyle very nicely in SwiftUI like so:
Text("Some text here")
.foregroundStyle(boolValue ? Color.green : Color.blue)
withAnimation {
boolValue.toggle()
}
However, if the foregroundStyle is a gradient, the color of the Text view changes immediately without animation.
The code below works to animate a gradient foregroundStyle on an SF Symbol, but it does not work when applied to a Text view. Is it possible to animate a Text view foregroundStyle between gradient values?
Image(systemName: "pencil.circle.fill")
.foregroundStyle(boolValue ? .linearGradient(colors: [.red, .orange], startPoint: .top, endPoint: .bottom) : .linearGradient(colors: [.green, .blue], startPoint: .top, endPoint: .bottom))
Thanks for your help!
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,
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.
}
I have a share extension in my app, that shall allow users to send CSV files, custom app files, and selected text to my app via the share sheet for importing that data.
So, the share extension should activate when the user has selected either:
CSV or plain text files
Custom UTI app files
Text selected in other apps
The supported file types have been defined in as a predicate query according to the example in the docs
This works all fine on iOS, and the file sharing also works on the Mac.
However, on macOS, my app is not shown as a target in the share sheet when the user selects text in other apps and tries to share that text via the context menu.
Does macOS need a different configuration to enable a share extension for selected text?
This is how my Info.plist of the Mac share extension looks like:
...
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<string>SUBQUERY (
extensionItems,
$extensionItem,
SUBQUERY (
$extensionItem.attachments,
$attachment,
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text" ||
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "com.myCompany.myApp.customFormat" ||
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.delimited-values-text"
).@count == $extensionItem.attachments.@count
).@count >= 1</string>
</dict>
...
</dict>
</plist>
I know there is a NSExtensionActivationSupportsText but it seems this cannot be combined with a subquery rule.
Is there a way to explicitly enable text activation within the subquery rule?
I have created an AppIntent and added it to shortcuts to be able to read by Siri. When I say the phrase, the Siri intent dialog appears just fine. I have added a custom SwiftUI View inside Siri dialog box with 2 buttons with intents. The callback or handling of those buttons is not working when initiated via Siri. It works fine when I initiate it in shortcuts. I tried using the UIButton without the intent action as well but it did not work. Here is the code.
static let title: LocalizedStringResource = "My Custom Intent"
static var openAppWhenRun: Bool = false
@MainActor
func perform() async throws -> some ShowsSnippetView & ProvidesDialog {
return .result(dialog: "Here are the details of your order"), content: {
OrderDetailsView()
}
}
struct OrderDetailsView {
var body: some View {
HStack {
if #available(iOS 17.0, *) {
Button(intent: ModifyOrderIntent(), label : {
Text("Modify Order")
})
Button(intent: CancelOrderIntent(), label : {
Text("Cancel Order")
})
}
}
}
}
struct ModifyOrderIntent: AppIntent {
static let title: LocalizedStringResource = "Modify Order"
static var openAppWhenRun: Bool = true
@MainActor
func perform() async throws -> some OpensIntent {
// performs the deeplinking to app to a certain page to modify the order
}
}
struct CancelOrderIntent: AppIntent {
static let title: LocalizedStringResource = "Cancel Order"
static var openAppWhenRun: Bool = true
@MainActor
func perform() async throws -> some OpensIntent {
// performs the deeplinking to app to a certain page to cancel the order
}
}
Button(action: {
if let url = URL(string: "myap://open-order") {
UIApplication.shared.open(url)
}
}
I have a struct that holds an instance of UINavigationController:
struct NavigationController {
static let shared = UINavigationController()
}
I use NavigationController.shared to push and pop ViewControllers around the app, rather than using the ViewController's .navigationController property.
The issue I'm having is that when I pop I get new instances of my previous ViewController, this is my hierarchy:
(0) UIWindow
|
---- (1) NavigationController (is set as the UIWindow.rootViewController)
|
---- (2) UITabBarController (is set with NavigationController.shared.setViewControllers)
|
---- (3) ViewController (HomeVC) (is the first tab of the UITabController)
|
---- (4) ViewController (ScanVC) (is pushed into the stack by NavigationController.shared.pushViewController)
---- (5) ViewController (NotificationsVC)
---- (6) ViewController (SettingsVC)
I put a print statement in my HomeVC in the viewDidLoad method
My understanding is that the viewDidLoad should only be called once in the lifecycle of a ViewController
When I go back to the HomeVC from the ScanVC then the print always gets triggered which means I have a new instance of the HomeVC
This is the print statement I created inside the viewDidLoad method:
print("\(#function) View Did Load, instance: \(self)")
Here's the output from going back and forth from the HomeVC to ScanVC:
viewDidLoad() View Did Load, instance: <HomeVC: 0x118db0000>
viewDidLoad() View Did Load, instance: <HomeVC: 0x118db3100>
viewDidLoad() View Did Load, instance: <HomeVC: 0x118db0700>
Any one has any suggestions on how to fix this? Because ideally going back to the HomeVC should not instantiate a new ViewController.
I tested this on a small test project and viewDidLoad would only be triggered once when the ViewController was instantiated.
Platform Specs:
Xcode 16.2
Swift 6.0.3
iOS 18.2 + iOS Simulator 18.3.1
Issue:
Refer to the following code:
struct CustomView: View {
@Binding var prop: CustomStruct
init(prop p: Binding<CustomStruct>) {
_prop = p
}
init(isPreview: Bool) {
let p = CustomStruct()
_prop = .constant(p)
}
var body: some View {
VStack {
Text("hi")
}
}
}
#Preview {
CustomView(isPreview: true)
.preferredColorScheme(.dark)
}
The first constructor is for normal app functionality (and previews/functions correctly when used with the rest of the app in the ContentView preview tab). The second constructor is for previewing only CustomView in its own preview tab. This constructor does not work when previewing in the same file, as shown above. It triggers an ambiguous crash, stating that the diagnostic log (which obviously provides no clear information) should be checked.
I have isolated the issue to be in the Binding reassignment in the second constructor. Replacing CustomStruct with anything but another struct, like an enum or primitive, fixes the issue.
Note: This bug only occurs when previewing (either through the #Preview macro or PreviewProvider struct).
Hello. Recently, our app has been experiencing crashes with the message 'Attempting to attach window to an invalidated scene' when creating a UIWindow.
Our code stores the UIWindowScene provided in the scene(:willConnectTo:options:) function in a global variable and does not change the set scene until the scene(:willConnectTo:options:) function is called again. Additionally, we do not perform any actions related to the disconnect event.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
hudManager.setup(windowScene: windowScene)
// ...
}
func sceneDidDisconnect(_ scene: UIScene) {
// do nothing
}
In all crash logs, the activationState of the WindowScene is "unattached", so I initially thought that creating a UIWindow with a scene in the 'unattached' state should be avoided.
However, in the scene(_:willConnectTo:options:) function, the scene's state is also 'unattached', yet the UIWindow is created successfully here, which makes me think that deciding whether to create a window based on the activationState is incorrect.
I did find that trying to create a UIWindow with a scene after it has been disconnected causes a crash.
func sceneDidDisconnect(_ scene: UIScene) {
// Crash occur here and scene's state is `unattached`
let window = UIWindow(windowScene: scene as! UIWindowScene)
}
If the activationState alone cannot be used to determine the validity of a scene, is there another way to check the validity of a Scene? Thank you
I have an application named "XY" that has been launched in several countries. Now, I intend to launch it in Turkey, but we are facing legal issues preventing us from using "XY" as the app's display name. Following the documentation, I localized the app's display name to "ZX" for both Turkish and English (Turkey). However, when users change their device settings, they do not see an option for English (Turkey) language selection. I assumed that for Turkish users, English (Turkey) would be the default language, but this is not the case. Could someone please assist me in resolving this issue? I've investigated options for localizing the display name based on region, but it seems that this functionality isn't feasible on iOS. In contrast, it's relatively straightforward to achieve on Android platforms.
Using the new floating tab bar with ios18 on ipad with an RTL language, when the tabbar is correctly flipped and the right buttons become left buttons, the buttons are pushed off the screen on initial load but do come back after some navigating / reloading, have recreated in a fresh project.
I'm attempting to write a macOS version of https://stackoverflow.com/a/74935849/2178159.
From my understanding, I should be able to set the menu property of an NSResponder and it will automatically show on right click.
I've tried a couple things:
A: set menu on an NSHostingController's view - when I do this and right or ctrl click, nothing happens.
B: set menu on NSHostingController directly - when I do this I get a crash Abstract method -[NSResponder setMenu:] called from class _TtGC7SwiftUI19NSHostingControllerGVS_21_ViewModifier_...__. Subclasses must override
C: manually call NSMenu.popup in a custom subclasses of NSHostingController or NSView's rightMouseDown method - nothing happens.
extension View {
func contextMenu(menu: NSMenu) -> some View {
modifier(ContextMenuViewModifier(menu: menu))
}
}
struct ContextMenuViewModifier: ViewModifier {
let menu: NSMenu
func body(content: Content) -> some View {
Interaction_UI(
view: { content },
menu: menu
)
.fixedSize()
}
}
private struct Interaction_UI<Content: View>: NSViewRepresentable {
typealias NSViewType = NSView
@ViewBuilder var view: Content
let menu: NSMenu
func makeNSView(context: Context) -> NSView {
let v = NSHostingController(rootView: view)
// option A - no effect
v.view.menu = menu
// option B - crash
v.menu = menu
return v.view
}
func updateNSView(_ nsView: NSViewType, context: Context) {
// part of option A
nsView.menu = menu
}
}
I'm trying to combine a RotateGesture and a MagnifyGesture within a single SwiftUI view using SimultaneousGesture. My goal is to allow users to rotate and zoom an image (potentially at the same time). However, I’m running into a problem:
If only one gesture (say, the magnification) starts and finishes without triggering the other (rotation), it seems that the rotation gesture is considered "failed." After that, no further .onChanged or .onEnded callbacks fire for either gesture until the user lifts their fingers and starts over.
Here’s a simplified version of my code:
struct ImageDragView: View {
@State private var scale: CGFloat = 1.0
@State private var lastScale: CGFloat = 1.0
@State private var angle: Angle = .zero
@State private var lastAngle: Angle = .zero
var body: some View {
Image("Stickers3")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 100)
.rotationEffect(angle, anchor: .center)
.scaleEffect(scale)
.gesture(combinedGesture)
}
var combinedGesture: some Gesture {
SimultaneousGesture(
RotateGesture(minimumAngleDelta: .degrees(8)),
MagnifyGesture()
)
.onChanged { combinedValue in
if let magnification = combinedValue.second?.magnification {
let minScale = 0.2
let maxScale = 5.0
let newScale = magnification * lastScale
scale = max(min(newScale, maxScale), minScale)
}
if let rotation = combinedValue.first?.rotation {
angle = rotation + lastAngle
}
}
.onEnded { _ in
lastScale = scale
lastAngle = angle
}
}
}
If I pinch and rotate together (or just rotate), both gestures work as expected.
But if I only pinch (or, sometimes, if the rotation amount doesn’t meet minimumAngleDelta), subsequent gestures don’t trigger the .onChanged or .onEnded callbacks anymore, as if the entire gesture sequence is canceled.
I found that setting minimumAngleDelta: .degrees(0) helps because then rotation almost never fails. But I’d like to understand why this happens and whether there’s a recommended way to handle the situation where one gesture might be recognized but not the other, without losing the gesture recognition session entirely.
Is there a known workaround or best practice for combining a pinch and rotate gesture where either one might occur independently, but we still want both gestures to remain active?
Any insights would be much appreciated!
In UIKit, certain events like a button tap can be simulated using:
button.sendActions(for: .touchUpInside)
This allows us to trigger the button’s action programmatically.
However, in SwiftUI, there is no direct equivalent of sendActions(for:) for views like Button. What is the recommended approach to programmatically simulate a SwiftUI button tap and trigger its action?
Is there an alternative mechanism to achieve this(and for other events under UIControl.event) , especially in scenarios where we want to test interactions or trigger actions without direct user input?
I'm building an app using UITabbarController with 2 tabs: screen A and B. When standing on tab B and I taps on tab A, the order in which the events are triggered will be:
For iOS < 18:
viewWillDisappear() of screen B
tabBarController(_:didSelect:) of UITabbarController
For iOS >= 18:
tabBarController(_:didSelect:) of UITabbarController
viewWillDisappear() of screen B
So my question is this an issue or a new update from Apple on iOS 18.*?
Hi - I use TipKit in my App and AppClip. TipKit is configured with the app group's datastore. The tips show in the App, but on the AppClip, with the same rules/state, the tips do not display. Is this expected? TipKit is not listed as one of the frameworks unavailable to AppClips.
try? Tips.configure([
Tips.ConfigurationOption.displayFrequency(.hourly),
Tips.ConfigurationOption.datastoreLocation(.groupContainer(identifier: BuildConfiguration.shared.userDefaultsSuite))
])
Hello everyone,
I've run into a peculiar behavior with UINavigationController's setViewControllers on iOS 18.2 (I guess it might be reproducible on older versions) when reordering view controllers, and I wonder if anyone can shed some light on this issue.
Initial State: The navigation stack is [A - B - C].
Without Animation: Setting [A - C - B] updates the stack to: A - C - B as expected.
With Animation: Using the same command with animation changes the stack to [A - B], oddly omitting C.
Has anyone else noticed similar behavior or knows why animations might disrupt the stack's update this way? I'd appreciate any insights or suggestions.
Thanks, Dmytro
In the example code below:
OuterView has a @State var number: Int? = 0
InnerView expects a binding of type Int.
OuterView uses Binding.init(_: Binding<Value?>) to convert from Binding<Int?> to Binding<Int>?.
If OuterView sets number to nil, there is a runtime crash:
BindingOperations.ForceUnwrapping.get(base:) + 160
InnerView is being evaluated (executing body?) when number is nil despite the fact that it shouldn't exist in that configuration. Is this a bug or expected behavior?
struct OuterView: View {
@State var number: Int? = 1
var body: some View {
if let binding = Binding($number) {
InnerView(number: binding)
}
Button("Doesn't Crash") { number = 0 }
Button("Crashes") { number = nil }
}
}
struct InnerView: View {
@Binding var number: Int
var body: some View {
Text(number.formatted())
}
}
There is a workaround that involves adding an extension to Optional<Int>:
extension Optional<Int> {
var nonOptional: Int {
get { self ?? 0 }
set { self = newValue }
}
}
And using that to ensure that the binding has a some value even when number is nil.
if number != nil {
InnerView(number: $number.nonOptional)
}
This works, but I don't understand why it's necessary. Any insight would be greatly appreciated!
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hi,
Previously, we would conform model objects to the ObservableObject protocol and use the @StateObject property wrapper when storing them to an owned binding in a View.
Now, if I understand correctly, it is recommended that we use the new @Observable macro/protocol in place of ObservableObject and use the @State property wrapper rather than @StateObject. This is my understanding from documentation articles such as Migrating from the Observable Object protocol to the Observable macro.
However, the StateObject property wrapper has an initialiser which takes an autoclosure parameter:
extension StateObject {
public init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType)
}
This is an extremely important initialiser for state objects that are expensive to allocate. As far as I can tell, the @State property wrapper lacks an equivalent initialiser.
What is the recommended migration strategy for objects which made use of this on StateObject?
Thanks
I have view showing a list of contacts. When the user taps one, I want to raise a sheet that shows the contact's phone numbers and E-mail addresses and lets the user pick one.
When the user taps a list entry, I store the associated Contact object into a @State variable called selectedContact. Then I set the boolean that's bound to the sheet modifier's isPresented flag. That modifier:
.sheet(isPresented: $showContactMethodSheet, content: { ContactMethodView(withContact: selectedContact!) })
But the app crashes, because despite selectedContact having been set. It looks like the sheet was pre-built with a nil selected contact upon view load. Why, and what is the expected approach here?
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hello!
After upgrading to Xcode 16 & Swift 6 & iOS 18 I starting receiveing strange crashes.
Happens randomly in different view and pointing to onGeometryChange action block. I added DispatchQueue.main.async { in hopes it will help but it didn't.
HStack {
...
}
.onGeometryChange(for: CGSize.self, of: \.size) { value in
DispatchQueue.main.async {
self.width = value.width
self.height = value.height
}
}
As far as I understand, onGeometryChange is defined as nonisolated and Swift 6 enforce thread checking for the closures, SwiftUI views are always run on the main thread. Does it mean we can not use onGeometryChange safely in swiftui?
BUG IN CLIENT OF LIBDISPATCH: Assertion failed: Block was expected to execute on queue [com.apple.main-thread (0x1eacdce40)]
Crashed: com.apple.SwiftUI.AsyncRenderer
0 libdispatch.dylib 0x64d8 _dispatch_assert_queue_fail + 120
1 libdispatch.dylib 0x6460 _dispatch_assert_queue_fail + 194
2 libswift_Concurrency.dylib 0x62b58 <redacted> + 284
3 Grit 0x3a57cc specialized implicit closure #1 in closure #1 in PurchaseModalOld.body.getter + 4377696204 (<compiler-generated>:4377696204)
4 SwiftUI 0x5841e0 <redacted> + 60
5 SwiftUI 0x5837f8 <redacted> + 20
6 SwiftUI 0x586b5c <redacted> + 84
7 SwiftUICore 0x68846c <redacted> + 48
8 SwiftUICore 0x686dd4 <redacted> + 16
9 SwiftUICore 0x6ecc74 <redacted> + 160
10 SwiftUICore 0x686224 <redacted> + 872
11 SwiftUICore 0x685e24 $s14AttributeGraph12StatefulRuleP7SwiftUIE15withObservation2doqd__qd__yKXE_tKlF + 72
12 SwiftUI 0x95450 <redacted> + 1392
13 SwiftUI 0x7e438 <redacted> + 32
14 AttributeGraph 0x952c AG::Graph::UpdateStack::update() + 540
15 AttributeGraph 0x90f0 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, unsigned int) + 424
16 AttributeGraph 0x8cc4 AG::Subgraph::update(unsigned int) + 848
17 SwiftUICore 0x9eda58 <redacted> + 348
18 SwiftUICore 0x9edf70 <redacted> + 36
19 AttributeGraph 0x148c0 AGGraphWithMainThreadHandler + 60
20 SwiftUICore 0x9e7834 $s7SwiftUI9ViewGraphC18updateOutputsAsync2atAA11DisplayListV4list_AG7VersionV7versiontSgAA4TimeV_tF + 560
21 SwiftUICore 0x9e0fc0 $s7SwiftUI16ViewRendererHostPAAE11renderAsync8interval15targetTimestampAA4TimeVSgSd_AItF + 524
22 SwiftUI 0xecfdfc <redacted> + 220
23 SwiftUI 0x55c84 <redacted> + 312
24 SwiftUI 0x55b20 <redacted> + 60
25 QuartzCore 0xc7078 <redacted> + 48
26 QuartzCore 0xc52b4 <redacted> + 884
27 QuartzCore 0xc5cb4 <redacted> + 456
28 CoreFoundation 0x555dc <redacted> + 176
29 CoreFoundation 0x55518 <redacted> + 60
30 CoreFoundation 0x55438 <redacted> + 524
31 CoreFoundation 0x54284 <redacted> + 2248
32 CoreFoundation 0x535b8 CFRunLoopRunSpecific + 572
33 Foundation 0xb6f00 <redacted> + 212
34 Foundation 0xb6dd4 <redacted> + 64
35 SwiftUI 0x38bc80 <redacted> + 792
36 SwiftUI 0x1395d0 <redacted> + 72
37 Foundation 0xc8058 <redacted> + 724
38 libsystem_pthread.dylib 0x637c _pthread_start + 136
39 libsystem_pthread.dylib 0x1494 thread_start + 8
I set the titleView of a view controller to a UISlider like so in viewDidLoad:
UISlider *slider = [UISlider new];
self.navigationItem.titleView = slider;
The desired outcome is that the slider takes the full width of the title view. This is working fine when the view is loaded in the wider landscape mode. The slider adjust its size as expected when rotating to portrait mode.
However, when the view is loaded in the narrower portrait mode and then the device is rotated to landscape, the slider does not grow in width to make use of the newly available size.
Why is that so and how can it get the desired outcome as described?
After viewDidLoad:
After rotating: