Is there a trick to getting the new Live Activities on Apple Watch working via the simulator? I have an iPhone paired to a watch simulator, but live activities created on the phone aren't appearing on the watch Smart Stack.
It looks like maybe the Smart Stack isn't functional on the watch simulator, as there are a bunch of blank tiles where the Smart Stack should be.
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
I am trying to seamlessly transition a view from one view controller and have it "jump" into a sheet.
Apple does this in their widget picker for example when you add one of the top widgets.
Based on all the documentation I've read, the most common approach to this is to snapshot the area/view that you are trying to seamlessly transition in the presented view controller (the place where the item is coming from. And then have that snapshot translate across into where it should be in the layout of the presenting view controller.
func makeAnimatorIfNeeded(
using transitionContext: UIViewControllerContextTransitioning
) -> UIViewPropertyAnimator {
if let animator = animator {
return animator
}
// Presentation animation
let animator = UIViewPropertyAnimator(
duration: 0.5,
timingParameters: UISpringTimingParameters(dampingRatio: 1.0)
)
guard let fromVC = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to)
else {
transitionContext.completeTransition(false)
return animator
}
/// !IMPORTANT: For some weird reason, accessing the views of the view controller
/// directly, like `fromVC.view` or `toVC.view` will break the sheet transition and gestures.
/// Instead, use the `view(forKey:)` method of the `transitionContext` to get the views.
let fromView = transitionContext.view(forKey: .from)
let toView = transitionContext.view(forKey: .to)
let containerView = transitionContext.containerView
let fromFrame = transitionContext.viewController(forKey: .from).map { transitionContext.finalFrame(for: $0) } ?? containerView.bounds
let toFrame = transitionContext.viewController(forKey: .to).map { transitionContext.finalFrame(for: $0) } ?? containerView.bounds
// Calculate the frame of sourceView relative to fromVC.view to capture the correct snapshot area.
let snapshotFrameInFromVC = sourceView?.convert(sourceView?.frame ?? .zero, to: fromVC.view) ?? containerView.frame
// Calculate the frame of sourceView relative to containerView to position the snapshot correctly.
let snapshotFrameInContainer = sourceView?.convert(sourceView?.frame ?? .zero, to: containerView) ?? containerView.frame
let snapshot: UIView?
if isPresenting {
// Create a snapshot of fromVC.view from the defined area (snapshotFrameInFromVC).
let originalColor = fromVC.view.backgroundColor
fromVC.view.backgroundColor = .clear
snapshot = fromVC.view.resizableSnapshotView(from: snapshotFrameInFromVC,
afterScreenUpdates: true,
withCapInsets: .zero)
fromVC.view.backgroundColor = originalColor
// Position the snapshot correctly within the snapshot container
snapshot?.frame = snapshotFrameInContainer
toView?.frame = toFrame
toView?.transform = CGAffineTransform(translationX: 0, y: containerView.frame.size.height)
toView?.layoutIfNeeded()
if let fromView {
containerView.addSubview(fromView)
}
if let toView {
containerView.addSubview(toView)
containerView.addSubview(snapshot ?? UIView())
}
let toViewCenter = CGPoint(
x: toVC.view.bounds.midX,
y: toVC.view.bounds.midY + 55
)
let gestureVelocity = CGPoint(x: 0, y: -5000)
animator.addAnimations {
Wave.animate(
withSpring: self.animatedSpring,
mode: .animated,
gestureVelocity: gestureVelocity
) {
snapshot?.animator.frame.size = CGSize(width: 204, height: 204) // Important to animate first
snapshot?.animator.center = toViewCenter
} completion: { finished, retargeted in
print("finished: \(finished), retargeted: \(retargeted)")
}
toView?.transform = CGAffineTransform.identity
}
animator.addCompletion { animatingPosition in
switch animatingPosition {
case .end:
snapshot?.removeFromSuperview()
transitionContext.completeTransition(true)
default:
transitionContext.completeTransition(false)
}
}
} else {
// Transitioning view is fromView
if let toView {
containerView.addSubview(toView)
}
if let fromView {
containerView.addSubview(fromView)
}
animator.addAnimations {
fromView?.transform = CGAffineTransform(translationX: 0, y: containerView.frame.size.height)
}
animator.addCompletion { animatingPosition in
switch animatingPosition {
case .end:
transitionContext.completeTransition(true)
default:
transitionContext.completeTransition(false)
}
}
}
self.animator = animator
return animator
}
I can pull this off seamlessly if the animation involves a simple movement from A to B.
But if I try to resize the snapshot as well, the snapshot becomes pixelated and low quality (I assume this is because the snapshot is literally a small screenshot and I'm stretching it from 56x56 to 204x204).
Is there another way that I'm overlooking to pull this off without a snapshot? Or is there a way I can resize the snapshot without losing quality?
Here is the animator code, it works great without the sizing so this should help future people looking to replicate the same effect anyways.
I am using SwiftUI NavigationView to navigate. I cant find how can i disable swipe from the leftmost part of the screen for navigation bar. Im tried this way , but it doesn't work for me:
struct DisableBackSwipeGestureView: UIViewControllerRepresentable {
typealias UIViewControllerType = UINavigationController
func makeUIViewController(context: Context) -> UINavigationController {
DisableSwipeBackViewController().navigationController ?? UINavigationController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
class Coordinator: NSObject, UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
}
final class DisableSwipeBackViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navigationController = self.navigationController {
if let interactivePopGestureRecognizer = navigationController.interactivePopGestureRecognizer {
interactivePopGestureRecognizer.delegate = self
interactivePopGestureRecognizer.isEnabled = false
}
}
}
}
extension DisableSwipeBackViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
extension View {
func disableSwipeBackGesture() -> some View {
self.modifier(DisableSwipeBackGesture())
}
}
Is there a way to disable this feature in swiftui?
I am using SwiftUI NavigationView to navigate. I cant find how can i disable swipe from the leftmost part of the screen for navigation bar. In tried this way , but it doesn't work for me:
struct DisableSwipeBackGesture: ViewModifier {
func body(content: Content) -> some View {
content
.background(DisableBackSwipeGestureView())
}
}
struct DisableBackSwipeGestureView: UIViewControllerRepresentable {
typealias UIViewControllerType = DisableSwipeBackViewController
func makeUIViewController(context: Context) -> DisableSwipeBackViewController {
DisableSwipeBackViewController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
class Coordinator: NSObject, UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
}
final class DisableSwipeBackViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navigationController = self.navigationController {
if let interactivePopGestureRecognizer = navigationController.interactivePopGestureRecognizer {
interactivePopGestureRecognizer.delegate = self
interactivePopGestureRecognizer.isEnabled = false
}
}
}
}
extension DisableSwipeBackViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
extension View {
func disableSwipeBackGesture() -> some View {
self.modifier(DisableSwipeBackGesture())
}
}
Is there a way to disable this feature?
In the company that I work we have a huge app and we have implemented an image annotation feature using the QLPreviewController but there is a bug where even with an OOTB implementation of the QLPreviewController, when I add a text, change the colour of that text and then double click on the text to edit it, it reverts back to black.
I tried the same implementation in a sample app but I couldn't reproduce the bug hence it is not a bug with the library but something with the app interacting with the library. I was wondering if anyone can provide any suggestion with what the reason may be, as sadly I cannot provide any code examples as that would break company code.
From my research what I found strange is that when I double click the coloured text and it goes to black, then undo it its as if that was not a recorded event as the undo goes back to the step before the text was coloured meaning it replaces the colour changing event rather than recording it as a new event that can be undone.
If any has any suggestion please help me as I am quite stuck. 🙏
Hello !
Since iOS/iPadOS 18 there is a new switch transition between tabs in UITabBarController where is a little zoom of the selected UIViewController.
At first, I thought that was a bug but I found the same animation on the Apple's apps (like music, shortcuts...)
On my app, this animation produce a little flash/blink white before zooming, it's not smooth like on Apple's apps.
I searched on documentation but I didn't found any topics about how to disable it or handled it better.
Is this possible to disabled it ?
I'd like to emulate the behavior of UIViewController.isModalInPresentation in SwiftUI. In my first attempt, I defined the following view:
struct ModalView<Content: View>: UIViewControllerRepresentable {
		var content: () -> Content
		func makeUIViewController(context: Context) -> UIHostingController<Content> {
				let controller = UIHostingController(rootView: content())
				controller.isModalInPresentation = true
				return controller
		}
		func updateUIViewController(_ imagePickerController: UIHostingController<Content>, context: Context) {}
}
From my main app view, I then present the ModalView as a sheet:
struct ContentView: View {
		@State
		var presentSheet: Bool = true
		var body: some View {
				Text("Hello, world!")
						.sheet(isPresented: $presentSheet) {
								ModalView {
										Text("Sheet")
								}
						}
		}
}
But the user is still able to dismiss the ModalView by swiping down. I would expect this sheet to be non-dismissible. Is anything like this supposed to work? If not, is there some other way to prevent the dismissal of a sheet in SwiftUI?
The closest workaround I've found is to apply .highPriorityGesture(DragGesture()) to the content of the sheet, but swiping down with two fingers still works.
Is there a time limit for the perform method execution in iOS? I'm implementing App Intents that call an API from the perform method. Based on my testing, it works fine if the API completes within 10 seconds. However, if it takes longer, Siri ends the process with an error.
When trying to look for an app in the search view, when write only the letter S everything is crashing and cleaning the search
https://photos.app.goo.gl/A7HfaPG1oQGUCTk89
My problem: I tap in one of the TextFields defined below and try to type. After typing a single character, the cursor disappears from the TextField and I need to tap in the field again to enter the next character.
I have the following code:
@Observable
final class Species {
. . .
public var list: [String]
. . .
}
struct SpeciesCapture: View {
@State private var species = Species()
@State var trainingList: [String] = []
var body: some View {
NavigationStack {
VStack(alignment: .leading) {
HStack {
Text("some text")
Spacer()
Button("Add") {
trainingList.append("")
}
}
List {
ForEach($trainingList, id: \.self) { $animal in
TextField("Animal", text: $animal)
.textCase(.lowercase)
}
}
}
.onAppear {
trainingList = species.list
. . .
}
. . . // etc.
}
}
}
It appears that I am continually losing focus. How do I fix this problem?
Xcode 16.0 targeting iOS 18. The behavior appears in both Preview and the Simulator. (Haven't tried it on a physical device).
I've seen several users experiencing an app crash that I've been unable to find the root cause of so far. I've tried manual symbolication but have been unable to reproduce it. It only occurs for iPad device running on iOS 17.6.1. Any ideas on how I can troubleshoot this?
crash_log.txt
I have been using the following function in my UITabBarController for several years, which runs some code when the user clicks a tab. I have now built the app using Xcode 16 and when I run it on my iPad with iPadOS 18, the function is not called. When I run the same app on iPadOS 16, it works just fine like it always has.
Is this a bug, or is there something new that I need to do for iOS 18 support?
override func tabBar(_tabBar: UITabBar, didSelect item: UITabBarItem) {
// Some code
}
NOTE: My UITabBarController is presented from my root view controller, which is a UINavigationController.
My app uses a temporary singleton to store CKRecords for user profiles, to prevent repeated fetching of profile pics & display usernames during a user's session.
Since iOS 18, after leaving the app in the background for an unspecified period of time & reopening it, the app has started to discard the CKAssets in those 'cached' records.
The records are still there, & the custom fields such as the display username string is still accessible. However the profile pic assets aren't?
This is the code that is displaying the profile picture, could this be something to do with some changes to how CKAssets are given file urls?
if postOwnerRecord != nil, let imageAsset = postOwnerRecord!.object(forKey: "profilePicture") as? CKAsset, let photoData = NSData(contentsOf:imageAsset.fileURL!) {
if let uiImage = UIImage(data: photoData as Data) {
let imageToUse = Image(uiImage: uiImage)
Image(uiImage: imageToUse)
}
}
Expected behavior: CKAssets should persist when resuming the app from the background.
Actual behavior: CKAssets are discarded when reopening the app, but custom fields are still accessible.
Question: Is this related to iOS 18, or am I mishandling how CKAssets are cached or their file URLs? Is there a better approach to caching these assets across app sessions? Any pointers or changes would be appreciated.
I've reviewed iOS 18 release notes but didn't find any clear references to changes with CKAsset handling. Any ideas?
I have defined a colour in the asset catalog of my widget extension. I am using this widget extension for my live activity. This colour has both an ‘Any’ and ‘Dark’ appearance. I am using this colour in the activityBackgroundTint modifier for my main live activity view (the ActivityPreviewViewKind is content, visible on the Lock Screen). In light mode, this works as expected, but in dark mode, the background colour is still the light mode variant. The content within the view changes according to whether the system is in light or dark mode, but the background does not.
I want to avoid using just the background modifier instead of activityBackgroundTint, as this does not colour the ‘Allow Live Activities from …’ and ‘Clear’ sections, which is undesirable.
I have also tried reading colorScheme from the environment and choosing the light or dark mode colour variant accordingly, but this workaround also doesn’t completely work. If you keep changing from light and dark mode (via the Settings app, not the control centre, for whatever reason), the background colour actually becomes the opposite of the expected value in this case.
Is there a workaround that can help me achieve what I want, or am I simply doing something wrong?
I have filed a bug report under FB15148099
Dear all, how do I add any extra buttons to the AVPlayer / VideoPlayer menu? I would like to add a button e.g. linking to a particular website.
Thank you!
Hello,
we have weird crash in our app that mostly seems to happen right after launch. It is quite rare and so far I haven’t been able to reproduce it (the info below comes from Crashlytics).
The main error message I have is this:
failed to demangle witness for associated type 'Property' in conformance 'SwiftUI.StateObject<AppBlock.QuickBlockActivityViewModel>.(unknown context at $18f34e5b8).Box: DynamicPropertyBox' from mangled name ' � ��yxG' - subject type x does not conform to protocol ObservableObject
And here is the stack trace:
Crashed: com.apple.main-thread
0 libsystem_kernel.dylib 0x7200 __pthread_kill + 8
1 libsystem_pthread.dylib 0x71ac pthread_kill + 268
2 libsystem_c.dylib 0x20ca0 abort + 180
3 libswiftCore.dylib 0x3d7304 swift::fatalError(unsigned int, char const*, ...) + 134
4 libswiftCore.dylib 0x3d7324 swift::warningv(unsigned int, char const*, char*) + 30
5 libswiftCore.dylib 0x3ee678 swift_getAssociatedConformanceWitnessSlowImpl(swift::TargetWitnessTable<swift::InProcess>*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*) + 2078
6 libswiftCore.dylib 0x3ecb9c swift_getAssociatedTypeWitness + 236
7 SwiftUI 0x5b838 OUTLINED_FUNCTION_49 + 640
8 SwiftUI 0xa8d68 OUTLINED_FUNCTION_513 + 16260
9 SwiftUI 0x58244 OUTLINED_FUNCTION_177 + 10892
10 SwiftUI 0x95524 OUTLINED_FUNCTION_1160 + 6632
We are using the view model (QuickBlockActivityViewModel) in a SwiftUI view that is part of collection view using the new UIHostingConfiguration from iOS 16.
Our view model is a subclass of view model for older iOS versions that conforms to ObservableObject and is marked as @MainActor.
And the view model is used like this:
@StateObject private var viewModel = QuickBlockActivityViewModel()
Internally the view model uses Combine to monitor a couple of states from other parts of the app to modify its properties.
Setting bundle data(pList)is not displaying under our app after iPadOS 18 upgrade. Before the iPadOS upgrade the settings was appearing in the app setting sections. Setting section is broken after the iPadOS 18 upgrade. Is this the known issue and how do we get the setting section back?
Topic:
UI Frameworks
SubTopic:
General
I am using NSMenuToolbarItem to show a drop-down menu in my NSToolbar. This works as expected when creating an NSMenu beforehand and assign it to the menu property of NSMenuToolbarItem.
However, my menu needs to be built dynamically when the user clicks the dropdown button. To do that, I am using NSMenuDelegate. When creating the menu in the menuNeedsUpdate of the delegate, the first menu item isn't shown to the user. Why?
Menu when using delegate:
Menu when pre-assigning menu:
I also cannot just add a placeholder menu item at the start of the NSMenuToolbarItem as all menu items do show in the overflow menu.
Example code:
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate, NSToolbarDelegate, NSMenuDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 400, height: 300),
styleMask: [.titled, .closable, .resizable],
backing: .buffered,
defer: false)
window.makeKeyAndOrderFront(nil)
let toolbar = NSToolbar(identifier: "MainToolbar")
toolbar.delegate = self
window.toolbar = toolbar
}
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [NSToolbarItem.Identifier("item1"), NSToolbarItem.Identifier("item2")]
}
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [NSToolbarItem.Identifier("item1"), NSToolbarItem.Identifier("item2")]
}
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar: Bool) -> NSToolbarItem? {
let item = NSMenuToolbarItem(itemIdentifier: itemIdentifier)
if itemIdentifier == NSToolbarItem.Identifier("item1") {
let menu = NSMenu()
fillMenuWithItems(menu)
item.menu = menu
} else if itemIdentifier == NSToolbarItem.Identifier("item2") {
item.menu = NSMenu()
item.menu.delegate = self
}
return item
}
func menuNeedsUpdate(_ menu: NSMenu) {
menu.removeAllItems()
fillMenuWithItems(menu)
}
func fillMenuWithItems(_ menu: NSMenu) {
menu.addItem(NSMenuItem(title: "Option 1", action: nil, keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "Option 2", action: nil, keyEquivalent: ""))
}
}
In SPM package test running on visionOS 2, when creating a UISwitch we get a crash / error:
Nil UISwitch visual element provided by <UISwitch: 0x102b15590; frame = (0 0; 0 0); layer = <CALayer: 0x6000004c1780>> (NSInternalInconsistencyException)
This issue only occurs on visionOS 2 in Xcode 16 with Swift Package Manager. It works fine when running on visionOS 1.2 in Xcode 16 with SPM, as well as on iOS 18 in Xcode 16 with SPM. Additionally, running the test on visionOS inside of a non-SPM package (Project test) is also fine.
What results you expected - The test should pass for UISwitch initialization on SPM for visionOS 2 on Xcode 16.
What results you actually saw - Test failure with error - Nil UISwitch visual element provided by <UISwitch: 0x102b15590; frame = (0 0; 0 0); layer = <CALayer: 0x6000004c1780>> (NSInternalInconsistencyException)
The version of Xcode you are using - Xcode 16
Feedback id - FB15254532
I'm presenting a UIKit view controller from SwiftUI in a sheet using UIViewControllerRepresentable. The size of the sheet is being set to PagePresentationSizing using the new iOS 18 presentationSizing method.
When Split View is used and the size class changes from regular to compact, the sheet resizes as expected to fit in the smaller window. When the app returns to full screen and the size class changes back to regular, the sheet is now displayed using FormPresentationSizing instead of PagePresentationSizing.
This all worked as expected in iOS 17 with the view controller modalPresentationStyle being specified in the UIViewControllerRepresentable implementation, but the behaviour has now changed with iOS 18.
How do I preserve the desired presentation sizing of the sheet?
Thanks for any help.