Hello everyone,
I'm working on a macOS application and encountering an issue related to multi-screen setups.
My application includes an animation in the menu bar, and it works perfectly on the primary screen. However, I want this animation to stop or not appear when the app is being used on a secondary monitor(non-focused monitor).
(for cpu related problem, when it's 3 monitor -> x3 cpu with x3 animation objects)
I've tried a few approaches to detect when the application is being used on a secondary screen and then stop the animation, but so far, I haven't had any success.
The main challenge seems to be accurately determining the screen where the app is actively being used and controlling the animation accordingly.
Has anyone faced a similar issue or have suggestions on how to effectively stop or prevent the menu bar animation from running on a secondary screen? Any guidance or insights would be greatly appreciated.
Thank you in advance for your help!
Core Animation
RSS for tagRender, compose, and animate visual elements using Core Animation.
Posts under Core Animation tag
43 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Having an issue with crashes on iOS 16 and iOS 17 devices. App works and will appear to operate normally. However after a while the app will crash with a EXC_BREAKPOINT on libobjc.A.dylib object_getClass. The root cause seems to be something related to UIViews/Core Animation, I think. However the root crash trigger seems to be a half a dozen different triggers from the UI on main thread. It also only triggers on release builds, or the dev builds take a lot longer to trigger to the point it's very difficult to reproduce.
Here are some examples of the crashes occurring.
Crashed: com.apple.main-thread
0 libobjc.A.dylib 0xe838 object_getClass + 48
1 Foundation 0x1c854 _NSKeyValueObservationInfoGetObservances + 264
2 Foundation 0x1bd1c NSKeyValueWillChangeWithPerThreadPendingNotifications + 232
3 QuartzCore 0x59408 CAAnimation_setter(CAAnimation*, unsigned int, _CAValueType, void const*) + 128
4 QuartzCore 0x749b4 -[CAAnimation setBeginTime:] + 52
5 QuartzCore 0x728b4 CA::Layer::commit_animations(CA::Transaction*, double (*)(CA::Layer*, double, void*), void (*)(CA::Layer*, CA::Render::Animation*, void*), void (*)(CA::Layer*, __CFString const*, void*), CA::Render::TimingList* (*)(CA::Layer*, void*), void*) + 740
6 QuartzCore 0x2aeac invocation function for block in CA::Context::commit_transaction(CA::Transaction*, double, double*) + 148
7 QuartzCore 0x2adb4 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 368
8 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
9 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
10 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
11 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
12 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
13 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
14 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
15 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
16 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
17 QuartzCore 0x2ad40 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
18 QuartzCore 0x6f548 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 11192
19 QuartzCore 0x65e3c CA::Transaction::commit() + 648
20 UIKitCore 0x924cc _afterCACommitHandler + 84
21 CoreFoundation 0x3583c __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36
22 CoreFoundation 0x34244 __CFRunLoopDoObservers + 548
23 CoreFoundation 0x33960 __CFRunLoopRun + 1028
24 CoreFoundation 0x33478 CFRunLoopRunSpecific + 608
25 GraphicsServices 0x34f8 GSEventRunModal + 164
26 UIKitCore 0x22c62c -[UIApplication _run] + 888
27 UIKitCore 0x22bc68 UIApplicationMain + 340
0 libobjc.A.dylib 0x5d94 object_getClass + 48
1 Foundation 0xadb8 _NSKeyValueObservationInfoGetObservances + 248
2 Foundation 0x3f670 NSKeyValueWillChangeWithPerThreadPendingNotifications + 232
3 QuartzCore 0xbb18 CAAnimation_setter(CAAnimation*, unsigned int, _CAValueType, void const*) + 128
4 QuartzCore 0xcc14 -[CAPropertyAnimation setKeyPath:] + 52
5 QuartzCore 0xcbc0 +[CAPropertyAnimation animationWithKeyPath:] + 36
6 UIKitCore 0xc89fb0 -[UIMorphingLabel animateGlyph:toScale:delay:] + 308
7 UIKitCore 0xc8a1fc -[UIMorphingLabel animateShowGlyph:alpha:alphaDuration:delay:] + 196
8 UIKitCore 0xc8b3f0 -[UIMorphingLabel animateSubstitutionAlignmentHunkAtIndex:] + 1068
9 UIKitCore 0x42cd84 -[UIMorphingLabel animateGlyphs] + 276
10 UIKitCore 0xa374 -[UIMorphingLabel layoutSubviews] + 368
11 UIKitCore 0x4420 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1992
12 QuartzCore 0x9f30 CA::Layer::layout_if_needed(CA::Transaction*) + 500
13 QuartzCore 0x1d4ac CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 148
14 QuartzCore 0x2e8d8 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 444
15 QuartzCore 0x5de80 CA::Transaction::commit() + 648
16 QuartzCore 0x47df0 CA::Transaction::flush_as_runloop_observer(bool) + 88
17 CoreFoundation 0x90234 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36
18 CoreFoundation 0x1a410 __CFRunLoopDoObservers + 532
19 CoreFoundation 0x7a19c __CFRunLoopRun + 1028
20 CoreFoundation 0x7f3ec CFRunLoopRunSpecific + 612
21 GraphicsServices 0x135c GSEventRunModal + 164
22 UIKitCore 0x39cf58 -[UIApplication _run] + 888
23 UIKitCore 0x39cbbc UIApplicationMain + 340
I'm at a loss, I'm clearly doing something wrong in the UIView/Core Animation side of things. Would be very grateful for anyone who can point me in the right direction.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.
I have been experiencing frequent crashes on iOS 17 and above recently, and I'm having trouble pinpointing the source of the crashes. The crash logs provide no information about the specific location of the crashes. Is there any workaround or solution to identify and resolve this issue?
Please find attached the crash logs below
I've started seeing several users getting an app crash that I've been unable to find the root cause for so far. I've tried running the app in release build with address sanitizer and zombie objects checks enabled but have been unable to reproduce it. It only occurs for iOS 17 users. Any ideas on how I can troubleshoot this?
Crashed: com.apple.main-thread
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000000
Crashed: com.apple.main-thread
0 libsystem_platform.dylib 0xed4 _platform_memmove + 52
1 QuartzCore 0x137120 CA::Render::InterpolatedFunction::encode(CA::Render::Encoder*) const + 248
2 QuartzCore 0x136f40 CA::Render::GradientLayer::encode(CA::Render::Encoder*) const + 44
3 QuartzCore 0x2e384 CA::Render::Layer::encode(CA::Render::Encoder*) const + 284
4 QuartzCore 0x2e224 CA::Render::encode_set_object(CA::Render::Encoder*, unsigned long, unsigned int, CA::Render::Object*, unsigned int) + 196
5 QuartzCore 0x2b654 invocation function for block in CA::Context::commit_transaction(CA::Transaction*, double, double*) + 244
6 QuartzCore 0x2b4fc CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 368
7 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
8 QuartzCore 0x2b4bc CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 304
9 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
10 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
11 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
12 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
13 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
14 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
15 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
16 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
17 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
18 QuartzCore 0x2b488 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block_pointer) + 252
19 QuartzCore 0x6fc60 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 11192
20 QuartzCore 0x66574 CA::Transaction::commit() + 648
21 UIKitCore 0x31b5ec __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 36
22 CoreFoundation 0x373a8 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28
23 CoreFoundation 0x35b9c __CFRunLoopDoBlocks + 356
24 CoreFoundation 0x33a9c __CFRunLoopRun + 848
25 CoreFoundation 0x33668 CFRunLoopRunSpecific + 608
26 GraphicsServices 0x35ec GSEventRunModal + 164
27 UIKitCore 0x22c2b4 -[UIApplication _run] + 888
28 UIKitCore 0x22b8f0 UIApplicationMain + 340
29 Coach 0x799d8 main + 14 (main.m:14)
30 ??? 0x1abefadcc (Missing)
I'm working on a SwiftUI project where I use a .sheet modifier to present a bottom drawer with two detent sizes: .medium and .large. The drawer contains a button that toggles its size between these two states. This part works as expected. However, I'm facing an animation issue when I set a non-default background color to the sheet.
Here's the simplified code for context:
struct ContentView: View {
@State private var selectedDetent: PresentationDetent = .medium
var body: some View {
VStack {}
.sheet(isPresented: .constant(true)) {
Button("Press Me") {
selectedDetent = selectedDetent == .medium ? .large : .medium
}
.presentationBackground(.gray)
.presentationDetents([.medium, .large], selection: $selectedDetent)
}
}
}
The issue arises when I try to set a background color using .presentationBackground(.gray) (or any color, even white). Instead of the expected behavior when pressing the button (where the upper part of the sheet closes, and the bottom part stays attached to the screen bottom), the sheet momentarily turns into a square in the middle of the screen before moving down to the .medium position.
As soon as I remove the.presentationBackground(.gray) line, it works as expected:
I tried several approaches, such as:
Using a custom background view.
Explicitly specifying animations.
Adjusting view hierarchy and layering.
Unfortunately, none of these solutions worked. The issue persists with any color. It seems like a bug or limitation in SwiftUI's handling of sheet animations with custom backgrounds.
Has anyone else encountered this issue, or does anyone have a workaround or solution?
Our DJ application Mixxx renders scrolling waveforms with 60 Hz. This looks perfectly smooth on an older 2015 MacBook Pro. However it looks jittery on a new M1 device with "ProMotion" enabled. Selecting 60 Hz fixes the issue.
We are looking for a way to tell macOS that it can expect 60 Hz renderings from Mixxx and must not display them early (at 120 Hz) even if the pictures are ready.
The alternative would be to read out the display settings and ask the user to select 60 Hz.
Is there an API to:
hint the display diver that we render with 60 Hz
read out the refresh rate settings?
I'm having issues animating views inside a ForEach list that's inside a border. Here's my code:
struct ContentView: View {
@State var showNewMethodOptions = false
var body: some View {
VStack {
Button {
showNewMethodOptions.toggle()
} label: {
HStack {
Image(systemName: showNewMethodOptions ? "chevron.up" : "plus")
.resizable()
.scaledToFit()
.frame(width: 32)
.foregroundColor(.blue)
Text("Add new payment method")
.foregroundColor(.gray)
.padding(.leading, 6)
Spacer()
}
.padding(.horizontal)
.padding(.top, 20)
.padding(.bottom, showNewMethodOptions ? 10 : 20)
}
if showNewMethodOptions {
ForEach(PaymentMethodType.allCases) { method in
NavigationLink {
} label: {
ZStack(alignment: .center) {
RoundedRectangle(cornerRadius: 5)
.stroke(.blue)
Text(method.rawValue)
.font(.title3)
.foregroundColor(.gray)
}
.frame(height: 45.0)
.background()
.padding(.horizontal)
.padding(.vertical, 5)
.transition(.slide)
}
}
}
}
.animation(.spring, value: showNewMethodOptions)
.padding(.bottom, showNewMethodOptions ? 16 : 0)
.padding(.horizontal)
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(.gray, lineWidth: 1)
.padding(.vertical, 4)
.padding(.horizontal)
)
}
}
enum PaymentMethodType: String, CaseIterable, Identifiable {
var id: Self {
return self
}
case checking = "Checking"
case savings = "Savings"
case creditOrDebitCard = "Card"
}
When the animation happens, the "add a new payment method" HStack animates nicely enough, but the payment method options fade in while it's still sliding up, which doesn't look great because it looks like everything's on top of each other. The options also don't seem to follow the transition animation I apply to them - it's always just a fade. Is there a way to get these elements to animate together?
In UIKit/Core Animation, I can create a CABasicAnimation that lets me animate changes to the Path in a CAShapeLayer. As long as the new path has the same number of control points as the previous path, Core Animation creates a smooth animation of the changes. It's also possible to animate things like the start and end of the stroke, but I'm talking about arbitrary changes to the path.
In SwiftUI, it's pretty easy to animate the stroke so that it looks like it's being drawn by a pen. I'm not talking about that. Say I have a closed path with N vertexes and some of the vertexes are connected with straight lines, while others are connected by quadratic or cubic Bézier curves. I want to move the control points around in arbitrary ways, and change the control points of the Bézier curves.
I have a simple Storyboard based project on Github that creates closed N-sided polygons with either rounded or sharp corners, it manages an array of PolygonPoint structs that look like this:
public struct PolygonPoint {
let point: CGPoint
let isRounded: Bool
let customCornerRadius: CGFloat?
init(point: CGPoint,
isRounded: Bool,
customCornerRadius: CGFloat? = nil) {
self.point = point
self.isRounded = isRounded
self.customCornerRadius = customCornerRadius
}
init(previousPoint: PolygonPoint, isRounded: Bool) {
self.init(point: previousPoint.point, isRounded: isRounded, customCornerRadius: previousPoint.customCornerRadius)
}
}
The sample app builds a square out of an array of PolygonPoints, and then presents 4 swtiches that controls whether each vertex is rounded or not. It builds a polygon using the CGPath addArc(tangent1End:tangent2End:radius:transform:) function, with a 0 radius for sharp corners and a non-zero corner radius for rounded corners. It wraps changes to the path in a CABasicAnimation, and the result is a simple animation.
The project can be found on Github here: RoundedConerPolygon project
As a learning exercise, I'd like to convert this project to SwiftUI. The Swift/UIKit project creates CGPaths, and there is an initializer for the Swift Path object that takes a CGPath, so that part is easy.
However, SwiftUI wants to observe changes to a simple scalar, or to some sort of animatableData. I've tried to make my PolygonPoint struct conform to VectorArithmetic, but no joy.
My attempt is here: RoundedCornerPolygonSwiftUI
This seems like it should be easy, but I can't figure out how to make it work. Is there a way to have SwiftUI animate changes to a Path's underlying CGPath like you can in UIKit? I could probably wrap a UIKIt custom view in SwiftUI and do it that way, but I'm trying to learn how to do it "The SwifTUI way."
This is verified to be a framework bug (occurs on Mac Catalyst but not iOS or iPadOS), and it seems the culprit is AVVideoCompositionCoreAnimationTool?
/// Exports a video with the target animating.
func exportVideo() {
let destinationURL = createExportFileURL(from: Date())
guard let videoURL = Bundle.main.url(forResource: "black_video", withExtension: "mp4") else {
delegate?.exporterDidFailExporting(exporter: self)
print("Can't find video")
return
}
// Initialize the video asset
let asset = AVURLAsset(url: videoURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
guard let assetVideoTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaType.video).first,
let assetAudioTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaType.audio).first else { return }
let composition = AVMutableComposition()
guard let videoCompTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)),
let audioCompTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
videoCompTrack.preferredTransform = assetVideoTrack.preferredTransform
// Get the duration
let videoDuration = asset.duration.seconds
// Get the video rect
let videoSize = assetVideoTrack.naturalSize.applying(assetVideoTrack.preferredTransform)
let videoRect = CGRect(origin: .zero, size: videoSize)
// Initialize the target layers and animations
animationLayers = TargetView.initTargetViewAndAnimations(atPoint: CGPoint(x: videoRect.midX, y: videoRect.midY), atSecondsIntoVideo: 2, videoRect: videoRect)
// Set the playback speed
let duration = CMTime(seconds: videoDuration,
preferredTimescale: CMTimeScale(600))
let appliedRange = CMTimeRange(start: .zero, end: duration)
videoCompTrack.scaleTimeRange(appliedRange, toDuration: duration)
audioCompTrack.scaleTimeRange(appliedRange, toDuration: duration)
// Create the video layer.
let videolayer = CALayer()
videolayer.frame = CGRect(origin: .zero, size: videoSize)
// Create the parent layer.
let parentlayer = CALayer()
parentlayer.frame = CGRect(origin: .zero, size: videoSize)
parentlayer.addSublayer(videolayer)
let times = timesForEvent(startTime: 0.1, endTime: duration.seconds - 0.01)
let timeRangeForCurrentSlice = times.timeRange
// Insert the relevant video track segment
do {
try videoCompTrack.insertTimeRange(timeRangeForCurrentSlice, of: assetVideoTrack, at: .zero)
try audioCompTrack.insertTimeRange(timeRangeForCurrentSlice, of: assetAudioTrack, at: .zero)
}
catch let compError {
print("TrimVideo: error during composition: \(compError)")
delegate?.exporterDidFailExporting(exporter: self)
return
}
// Add all the non-nil animation layers to be exported.
for layer in animationLayers.compactMap({ $0 }) {
parentlayer.addSublayer(layer)
}
// Configure the layer composition.
let layerComposition = AVMutableVideoComposition()
layerComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
layerComposition.renderSize = videoSize
layerComposition.animationTool = AVVideoCompositionCoreAnimationTool(
postProcessingAsVideoLayer: videolayer,
in: parentlayer)
let instructions = initVideoCompositionInstructions(
videoCompositionTrack: videoCompTrack, assetVideoTrack: assetVideoTrack)
layerComposition.instructions = instructions
// Creates the export session and exports the video asynchronously.
guard let exportSession = initExportSession(
composition: composition,
destinationURL: destinationURL,
layerComposition: layerComposition) else {
delegate?.exporterDidFailExporting(exporter: self)
return
}
// Execute the exporting
exportSession.exportAsynchronously(completionHandler: {
if let error = exportSession.error {
print("Export error: \(error), \(error.localizedDescription)")
}
self.delegate?.exporterDidFinishExporting(exporter: self, with: destinationURL)
})
}
Not sure how to implement a custom compositor that performs the same animations as this reproducible case:
class AnimationCreator: NSObject {
// MARK: - Target Animations
/// Creates the target animations.
static func addAnimationsToTargetView(_ targetView: TargetView, startTime: Double) {
// Add the appearance animation
AnimationCreator.addAppearanceAnimation(on: targetView, defaultBeginTime: AVCoreAnimationBeginTimeAtZero, startTime: startTime)
// Add the pulse animation.
AnimationCreator.addTargetPulseAnimation(on: targetView, defaultBeginTime: AVCoreAnimationBeginTimeAtZero, startTime: startTime)
}
/// Adds the appearance animation to the target
private static func addAppearanceAnimation(on targetView: TargetView, defaultBeginTime: Double = 0, startTime: Double = 0) {
// Starts the target transparent and then turns it opaque at the specified time
targetView.targetImageView.layer.opacity = 0
let appear = CABasicAnimation(keyPath: "opacity")
appear.duration = .greatestFiniteMagnitude // stay on screen forever
appear.fromValue = 1.0 // Opaque
appear.toValue = 1.0 // Opaque
appear.beginTime = defaultBeginTime + startTime
targetView.targetImageView.layer.add(appear, forKey: "appear")
}
/// Adds a pulsing animation to the target.
private static func addTargetPulseAnimation(on targetView: TargetView, defaultBeginTime: Double = 0, startTime: Double = 0) {
let targetPulse = CABasicAnimation(keyPath: "transform.scale")
targetPulse.fromValue = 1 // Regular size
targetPulse.toValue = 1.1 // Slightly larger size
targetPulse.duration = 0.4
targetPulse.beginTime = defaultBeginTime + startTime
targetPulse.autoreverses = true
targetPulse.repeatCount = .greatestFiniteMagnitude
targetView.targetImageView.layer.add(targetPulse, forKey: "pulse_animation")
}
}
I have an issue with my macOS objC app that uses CATextLayer instances that adapt to the app appearance (dark, light).
I had no problem with Ventura, so I suppose this is a Sonoma bug. But maybe I'm not doing the right things.
Within -updateLayer , I call
stringLayer.foregroundColor = NSColor.textColor.CGColor.
(stringLayer is an instance of CATextLayer.)
NSColor.textColor should adapt to the app appearance.
But the color doesn't always change when the app appearance changes. So the text would turn black in dark mode (hence illegible) and white in light mode when I toggle the mode in the system preferences.
To investigate wether the issues was specific to the system text color, I tried (again, within -updateLayer):
NSColor *color = [NSApp.effectiveAppearance.name isEqualToString:NSAppearanceNameDarkAqua]? NSColor.redColor : NSColor.greenColor;
stringLayer.foregroundColor = color.CGColor;
I basically get the same issue. The correct color shows when the app launches, but doesn't change the first time I toggle the mode (dark/light). So the wrong color associates with the app appearance (red with light mode and green with dark mode).
The code below works :
NSColor *color = [NSApp.effectiveAppearance.name isEqualToString:NSAppearanceNameDarkAqua]? NSColor.redColor : NSColor.greenColor;
NSDictionary *dic = @{NSFontAttributeName: [NSFont labelFontOfSize:10.0], NSForegroundColorAttributeName:color};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"test"
attributes: dic];
stringLayer.string = string;
But the code below doesn't. The layer text color doesn't change when the app appearance changes.
NSDictionary *dic = @{NSFontAttributeName: [NSFont labelFontOfSize:10.0], NSForegroundColorAttributeName:NSColor.textColor};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"test"
attributes: dic];
stringLayer.string = string;
Note that the issue appears to be specific to the foreground color. The background color (which I update in the same method) is always set properly.
My app is somehow getting into a state where it is spamming
CADisplayTimingsControl too many requests.
and
Unmatched CADisplayTimingsControl unregistration.
Does anyone know what these logs mean? Or how they might be triggered?
I’m trying to achieve vertical split view in SwiftUI. There are two views, top one is Map (MKMapView actually, wrapped with UIViewRepresentable) and bottom one for this example could be Color.red. Between them there’s a handle to resize the proportions. Initial proportions are 0.3 of height for bottom, 0.7 for map. When resizing map frame, it feels choppy and slow. Replacing MapView with any other view does not produce the same issue.
Issue appears only on my real device (iPhone 11 Pro Max) simulator works fine.
My app keeps getting crashed for the same reason in some iphone(iOS 17) and I need help. Here is the lastExceptionBacktrace.
"lastExceptionBacktrace" :
[
{"imageOffset":972256,"symbol":"__exceptionPreprocess","symbolLocation":164,"imageIndex":10},
{"imageOffset":179200,"symbol":"objc_exception_throw","symbolLocation":60,"imageIndex":7},
{"imageOffset":1516452,"symbol":"-[NSException initWithCoder:]","symbolLocation":0,"imageIndex":10},
{"imageOffset":195548,"symbol":"CA::Layer::set_position(CA::Vec2 const&, bool)","symbolLocation":168,"imageIndex":19},
{"imageOffset":195340,"symbol":"-[CALayer setPosition:]","symbolLocation":52,"imageIndex":19},
{"imageOffset":195204,"symbol":"-[CALayer setFrame:]","symbolLocation":396,"imageIndex":19},
{"imageOffset":409656,"symbol":"__26-[_UILabelLayer setFrame:]_block_invoke","symbolLocation":56,"imageIndex":12},
{"imageOffset":409388,"symbol":"-[_UILabelLayer _setFrameOrBounds:settingAction:]","symbolLocation":60,"imageIndex":12},
{"imageOffset":426392,"symbol":"-[_UILabelLayer setFrame:]","symbolLocation":104,"imageIndex":12},
{"imageOffset":104744,"symbol":"-[UIView _backing_setFrame:]","symbolLocation":240,"imageIndex":12},
{"imageOffset":97808,"symbol":"-[UIView(Geometry) setFrame:]","symbolLocation":296,"imageIndex":12},
{"imageOffset":106944,"symbol":"-[UILabel setFrame:]","symbolLocation":112,"imageIndex":12},
{"imageOffset":16705216,"symbol":"-[UITableViewHeaderFooterView layoutSubviews]","symbolLocation":220,"imageIndex":12},
{"imageOffset":209368,"symbol":"-[UIView(CALayerDelegate) layoutSublayersOfLayer:]","symbolLocation":1528,"imageIndex":12},
{"imageOffset":424072,"symbol":"CA::Layer::layout_if_needed(CA::Transaction*)","symbolLocation":500,"imageIndex":19},
{"imageOffset":221860,"symbol":"-[UIView(Hierarchy) layoutBelowIfNeeded]","symbolLocation":308,"imageIndex":12},
{"imageOffset":16700796,"symbol":"-[UITableViewHeaderFooterView _sizeThatFits:stripPaddingForAbuttingView:isTopHeader:]","symbolLocation":192,"imageIndex":12},
{"imageOffset":16733416,"symbol":"-[UISectionRowData _headerFooterSizeForSection:inTable:withTitle:detailText:isHeader:stripPaddingForAbuttingView:isTopHeader:]","symbolLocation":680,"imageIndex":12},
{"imageOffset":2595124,"symbol":"-[UISectionRowData refreshWithSection:tableView:tableViewRowData:]","symbolLocation":464,"imageIndex":12},
{"imageOffset":2604748,"symbol":"-[UITableViewRowData rectForFooterInSection:heightCanBeGuessed:]","symbolLocation":104,"imageIndex":12},
{"imageOffset":2604572,"symbol":"-[UITableViewRowData heightForTable]","symbolLocation":52,"imageIndex":12},
{"imageOffset":2599644,"symbol":"-[UITableView _updateContentSizeSkippingContentOffsetAdjustment:]","symbolLocation":168,"imageIndex":12},
{"imageOffset":16599748,"symbol":"-[UITableView _rebuildGeometryForcingRowDataUpdate:skipContentOffsetAdjustment:updateImmediatelyIfPossible:]","symbolLocation":140,"imageIndex":12},
{"imageOffset":16595412,"symbol":"-[UITableView setLayoutMargins:]","symbolLocation":204,"imageIndex":12},
{"imageOffset":16595180,"symbol":"-[UITableView _setDefaultLayoutMargins:]","symbolLocation":52,"imageIndex":12},
{"imageOffset":16595032,"symbol":"-[UITableView didMoveToWindow]","symbolLocation":288,"imageIndex":12},
{"imageOffset":74184,"symbol":"-[UIView(Internal) _didMoveFromWindow:toWindow:]","symbolLocation":1716,"imageIndex":12},
{"imageOffset":1969584,"symbol":"-[UIScrollView _didMoveFromWindow:toWindow:]","symbolLocation":88,"imageIndex":12},
{"imageOffset":73152,"symbol":"-[UIView(Internal) _didMoveFromWindow:toWindow:]","symbolLocation":684,"imageIndex":12},
{"imageOffset":71756,"symbol":"__45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke","symbolLocation":124,"imageIndex":12},
{"imageOffset":71520,"symbol":"-[UIView _postMovedFromSuperview:]","symbolLocation":484,"imageIndex":12},
{"imageOffset":66468,"symbol":"-[UIView(Internal) _addSubview:positioned:relativeTo:]","symbolLocation":2180,"imageIndex":12},
{"imageOffset":555240,"symbol":"-[UIWindow addRootViewControllerViewIfPossible]","symbolLocation":728,"imageIndex":12},
{"imageOffset":1651088,"symbol":"-[UIWindow setRootViewController:]","symbolLocation":364,"imageIndex":12},
{"imageOffset":27056,"imageIndex":2},
{"imageOffset":26016,"imageIndex":2},
{"imageOffset":2179544,"symbol":"-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]","symbolLocation":320,"imageIndex":12},
......
{"imageOffset":2433472,"symbol":"-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:]","symbolLocation":288,"imageIndex":12},
{"imageOffset":60072,"symbol":"-[FBSScene _callOutQueue_didCreateWithTransitionContext:completion:]","symbolLocation":324,"imageIndex":9},
{"imageOffset":59716,"symbol":"__92-[FBSWorkspaceScenesClient createSceneWithIdentity:parameters:transitionContext:completion:]_block_invoke.108","symbolLocation":280,"imageIndex":9},
{"imageOffset":54696,"symbol":"-[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]","symbolLocation":168,"imageIndex":9},
{"imageOffset":103264,"symbol":"__92-[FBSWorkspaceScenesClient createSceneWithIdentity:parameters:transitionContext:completion:]_block_invoke","symbolLocation":352,"imageIndex":9},
{"imageOffset":17152,"symbol":"_dispatch_client_callout","symbolLocation":20,"imageIndex":8},
{"imageOffset":32072,"symbol":"_dispatch_block_invoke_direct","symbolLocation":284,"imageIndex":8},
{"imageOffset":39404,"symbol":"FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK","symbolLocation":52,"imageIndex":9},
{"imageOffset":39276,"symbol":"-[FBSMainRunLoopSerialQueue _targetQueue_performNextIfPossible]","symbolLocation":240,"imageIndex":9},
{"imageOffset":38980,"symbol":"-[FBSMainRunLoopSerialQueue _performNextFromRunLoopSource]","symbolLocation":28,"imageIndex":9},
{"imageOffset":228044,"symbol":"CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION","symbolLocation":28,"imageIndex":10},
{"imageOffset":224584,"symbol":"__CFRunLoopDoSource0","symbolLocation":176,"imageIndex":10},
{"imageOffset":218460,"symbol":"__CFRunLoopDoSources0","symbolLocation":340,"imageIndex":10},
{"imageOffset":213560,"symbol":"__CFRunLoopRun","symbolLocation":828,"imageIndex":10},
{"imageOffset":212504,"symbol":"CFRunLoopRunSpecific","symbolLocation":608,"imageIndex":10},
{"imageOffset":13804,"symbol":"GSEventRunModal","symbolLocation":164,"imageIndex":11},
{"imageOffset":2290512,"symbol":"-[UIApplication _run]","symbolLocation":888,"imageIndex":12},
{"imageOffset":2288012,"symbol":"UIApplicationMain","symbolLocation":340,"imageIndex":12},
{"imageOffset":24608,"imageIndex":2},
{"imageOffset":23876,"symbol":"start","symbolLocation":2104,"imageIndex":13}
],
I'm trying to animate a shape (e.g. a circle) to follow a custom path, and struggling to find the best way of doing this.
I've had a look at the animation options from SwiftUI, UIKit and SpriteKit and all seem very limited in what paths you can provide. Given the complexity of my path, I was hoping there'd be a way of providing a set of coordinates in some input file and have the shape follow that, but maybe that's too ambitious.
I was wondering if this were even possible, and assuming not, if there were other options I could consider.
I want to generate a video from some images and that video should have some animation while changing one image to another.
is it possible with UIView.transition(with:duration:options:animations:completion:) ? If it possible, then how can I achieve this ?
I've encountered an animation glitch affecting the text for AxisMarks and AxisLabels in the SwiftUI Chart framework. Below are the sample project code and animated gif for reference:
(I'm not being able to attach the gif for some reason. Here's my StackOverflow post with a the gift: https://stackoverflow.com/questions/77007744/swiftui-chart-text-animation-glitch-with-axismarks-and-axislabels)
struct ContentView: View {
@State var isDisplayingAge = true
var body: some View {
ChartView(data: ChartDataSample, isDisplayingAge: isDisplayingAge)
.onTapGesture {
withAnimation {
isDisplayingAge.toggle()
}
}
}
}
struct ChartData: Identifiable {
let age: Int
let presence: Int
let grade: Int
let id = UUID()
}
struct ChartView: View {
let data: [ChartData]
let isDisplayingAge: Bool
let xAxis = [0, 50, 100]
var body: some View {
VStack {
Chart {
ForEach(data) { chartData in
BarMark(x: .value("X Axis", isDisplayingAge ? chartData.age : chartData.presence),
y: .value("Y Axis", chartData.grade))
}
}
.chartXAxis(content: {
AxisMarks(position: .bottom, values: xAxis.map{$0}) { data in
AxisValueLabel(String(xAxis[data.index]) + (isDisplayingAge ? " years" : " days"))
}
})
.chartXAxisLabel(content: {
Text(isDisplayingAge ? "Data: age" : "Data: presence")
})
}
.padding(.horizontal, 13)
}
}
As you can observe, when I tap the chart to trigger a content change with animation, the text for AxisMarks (e.g., x years / x days) and AxisLabels (e.g., Data: age / Data: presence) animates unusually. A portion of the text is temporarily replaced by an ellipsis ("...") during the animation.
Interestingly, this issue occurs only in one direction of the animation. When transitioning from "year" to "day" or from "presence" to "age," the text simply gets replaced without any noticeable animation.
Can anyone explain what's happening here? Ideally, I'd like the texts to either fade into each other or be replaced without any animation.
Thank you.
Wrote a transition code for some images. It can show image one by one. But I want to create a video with this kind of animations (Zoom in, Zoom out etc). How can I achieve this.
UIView.transition(with: self.imageView,
duration: 2.0,
options: .transitionCrossDissolve,
animations: {
self.imageView.image = UIImage(imageLiteralResourceName:
"image2")
}, completion: nil)
Hi. I'm currently working on a project, where I try to achieve the best possible UX when rendering an interactive scene using custom Metal renderer.
The problem I'm facing right now is that from what I've observed, the timing of how touch events (in touchesBegan/Moved/Ended/Cancelled) arrive with regards to DisplayLink callbacks is different on different devices using latest stable iOS.
Disregarding special cases (fast touches movement causing two touchesMoved along with a single touchedEnded arriving during a frame interval and having awkward timing), the general picture is this:
on 14 Pro:
vsync - (8 ms of nothing) - touches - vsync
on 11 Pro Max:
vsync - touches - (16 ms of nothing) - vsync
In order to minimize input-to-presentation latency and fit CPU + GPU work into one frame, I'd like to schedule drawing right when CADisplayLink callback arrives.
The problem is that at this point on 11 Pro Max I have no knowledge about whether touches arrive or not, so the best we could do is to process those touches and schedule redraw on next vsync, which causes almost two frames latency between input and draw that reflects changes caused by this input (33 ms)
On 14 Pro, touches are dispatched just before next vsync, so the actual latency is just one 120hz frame (8ms).
What are possible workarounds to achieve the same UX on iPhone 11 Pro Max?
I'm writing a multiplatform app that uses MapKit with custom tiles via MKTileOverlay. The map should have buttons for zooming in and out. Those buttons set the region of the MKMapView with animation parameter.
I want to make this animation faster. While good for generic change of a region, it feels too slow when changing the region for the zooming purposes.
In general, I'm working with SwiftUI. But because MKTileOverlay is not available there, the MKMapView is being accessed via the UIViewRepresentable and NSViewRepresentable respectively.
On iOS, to make animation faster (reduce animation time), I'm using
UIView.animate(withDuration: 0.2) {
mapView.setRegion(region, animated: true)
}
and it achieves the goal.
On macOS, I tried:
NSAnimationContext.runAnimationGroup { context in
context.duration = 0.2
mapView.setRegion(region, animated: true)
}
and:
CATransaction.begin()
CATransaction.setAnimationDuration(0.2)
mapView.setRegion(region, animated: true)
CATransaction.commit()
but without success. The default animation duration is not overridden.
I appreciate any input you could give me.
Alexey
Is it possible to disable NavigationLink transition animation in SwiftUI? To be more precisely. I want to disable it only for a few links/views. Not the whole app