Core Animation

RSS for tag

Render, compose, and animate visual elements using Core Animation.

Posts under Core Animation tag

38 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

SwiftUI ForEach Animation inside border
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?
1
0
786
Nov ’23
How to animate arbitrary changes to a SwiftUI Path?
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."
0
0
609
Oct ’23
How do I work around a Mac Catalyst framework bug where no Core Animation output is shown in an export session?
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") } }
1
0
611
Oct ’23
CATextLayer not updating foreground color property on appearance change
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.
0
0
569
Oct ’23
App Crashes On Launch in some iphone(iOS 17)
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} ],
6
0
1.7k
Oct ’23
Performance issues when resizing MapView
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.
0
0
654
Sep ’23
CA::Render::Encoder::grow - iOS 13 - some app crashes reported in Organizer
We have an iOS app in the App Store. Recently, we see in Organizer / Crashes for that app a new type of crash that seems bound to iOS 13 devices (21 devices affected in the last few weeks, all with iOS 13.1, .2, or .3). Xcode reports that the crashes occurred in QuartzCore, on CA::Render::Encoder::grow(unsigned long).Any idea what can it be and how it could be fixed? It doesn't look related to our code. Can it be a recent Apple bug? (We didn't have these crash reports before iOS 13.) Thank you in advance.Here is an extract from the crash log:OS Version: iPhone OS 13.3 (17C54) Release Type: User Baseband Version: n/a Report Version: 104 Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Triggered by Thread: 0 Thread 0 name: Thread 0 Crashed: 0 libsystem_kernel.dylib 0x0000000191d27ec4 __pthread_kill + 8 1 libsystem_pthread.dylib 0x0000000191c431d8 pthread_kill$VARIANT$mp + 136 (pthread.c:1458) 2 libsystem_c.dylib 0x0000000191b97844 abort + 100 (abort.c:110) 3 QuartzCore 0x00000001989af340 CA::Render::Encoder::grow(unsigned long) + 304 (render-coding.cpp:562) 4 QuartzCore 0x00000001989afa80 CA::Render::Encoder::encode_data_async(void const*, unsigned long, void (*)(void const*, void*), ... + 172 (render-coding.h:272) 5 QuartzCore 0x000000019886c358 CA::Render::Image::encode(CA::Render::Encoder*) const + 748 (render-image.cpp:401) 6 QuartzCore 0x000000019888510c CA::Render::Layer::encode(CA::Render::Encoder*) const + 112 (render-coding.h:388) 7 QuartzCore 0x00000001989b3c2c CA::Render::encode_set_object(CA::Render::Encoder*, unsigned long, unsigned int, CA::Render::Obje... + 192 (render-coding.cpp:2151) 8 QuartzCore 0x00000001988f78a4 invocation function for block in CA::Context::commit_transaction(CA::Transaction*, double) + 1568 (CAContextInternal.mm:1632) 9 QuartzCore 0x00000001989add88 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 364 (CALayer.mm:2647) 10 QuartzCore 0x00000001989add00 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 228 (CALayer.mm:2633) 11 QuartzCore 0x00000001989add00 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 228 (CALayer.mm:2633) 12 QuartzCore 0x00000001989add00 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 228 (CALayer.mm:2633) 13 QuartzCore 0x00000001989add00 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 228 (CALayer.mm:2633) 14 QuartzCore 0x00000001989add00 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 228 (CALayer.mm:2633) 15 QuartzCore 0x00000001989add00 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 228 (CALayer.mm:2633) 16 QuartzCore 0x00000001989add00 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 228 (CALayer.mm:2633) 17 QuartzCore 0x00000001989add00 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 228 (CALayer.mm:2633) 18 QuartzCore 0x00000001989add00 CA::Layer::commit_if_needed(CA::Transaction*, void (CA::Layer*, unsigned int, unsigned int) block... + 228 (CALayer.mm:2633) 19 QuartzCore 0x00000001988f6924 CA::Context::commit_transaction(CA::Transaction*, double) + 2872 (CAContextInternal.mm:2288) 20 QuartzCore 0x000000019891fc08 CA::Transaction::commit() + 676 (CATransactionInternal.mm:438) 21 UIKitCore 0x0000000195769698 -[_UIContextBinder updateBindableOrderWithTest:force:] + 704 (_UIContextBinder.m:280) 22 UIKitCore 0x0000000195769214 -[_UIContextBinder createContextsWithTest:creationAction:] + 100 (_UIContextBinder.m:233) 23 UIKitCore 0x00000001961d2510 -[UIWindowScene _prepareForResume] + 84 (UIWindowScene.m:712) 24 UIKitCore 0x00000001955e364c -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 860 (UIScene.m:1055) 25 UIKitCore 0x00000001955e45b8 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 220 (UIScene.m:1315) 26 UIKitCore 0x0000000195b57248 -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:] + 464 (UIApplicationSceneClientAgent.m:80) 27 FrontBoardServices 0x0000000197051248 -[FBSSceneImpl updater:didUpdateSettings:withDiff:transitionContext:completion:] + 544 (FBSSceneImpl.m:551) 28 FrontBoardServices 0x0000000197075d28 __88-[FBSWorkspaceScenesClient sceneID:updateWithSettingsDiff:transitionContext:completion:]_bloc... + 120 (FBSWorkspaceScenesClient.m:356) 29 FrontBoardServices 0x000000019705af04 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 232 (FBSWorkspace.m:357) 30 FrontBoardServices 0x0000000197075c5c __88-[FBSWorkspaceScenesClient sceneID:updateWithSettingsDiff:transitionContext:completion:]_bloc... + 184 (FBSWorkspaceScenesClient.m:355) 31 libdispatch.dylib 0x0000000191bfd184 _dispatch_client_callout + 16 (object.m:495) 32 libdispatch.dylib 0x0000000191ba5fd8 _dispatch_block_invoke_direct$VARIANT$mp + 224 (queue.c:466) 33 FrontBoardServices 0x000000019709a418 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 40 (FBSSerialQueue.m:173) 34 FrontBoardServices 0x000000019709a0e4 -[FBSSerialQueue _queue_performNextIfPossible] + 404 (FBSSerialQueue.m:216) 35 FrontBoardServices 0x000000019709a60c -[FBSSerialQueue _performNextFromRunLoopSource] + 28 (FBSSerialQueue.m:247) 36 CoreFoundation 0x0000000191eaea00 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24 (CFRunLoop.c:1922) 37 CoreFoundation 0x0000000191eae958 __CFRunLoopDoSource0 + 80 (CFRunLoop.c:1956) 38 CoreFoundation 0x0000000191eae0f0 __CFRunLoopDoSources0 + 180 (CFRunLoop.c:1992) 39 CoreFoundation 0x0000000191ea923c __CFRunLoopRun + 1080 (CFRunLoop.c:2882) 40 CoreFoundation 0x0000000191ea8adc CFRunLoopRunSpecific + 464 (CFRunLoop.c:3192) 41 GraphicsServices 0x000000019be2e328 GSEventRunModal + 104 (GSEvent.c:2246) 42 UIKitCore 0x0000000195fa3ae0 UIApplicationMain + 1936 (UIApplication.m:4773) 43 <Our app name> 0x0000000100d9d32c main + 88 (main.m:14) 44 libdyld.dylib 0x0000000191d32360 start + 4
4
1
2.3k
Sep ’23
SwiftUI Chart Text Animation Glitch with AxisMarks and AxisLabels
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.
1
0
817
Aug ’23
How to generate video with animation using swift
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)
0
0
605
Aug ’23
CADisplayLink and UITouch events
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?
0
1
590
Aug ’23
An infinite CANVAS
I needed an infinite canvas for my app which is basically a drawing board where one can draw things using pen. So, I thought of having a very large custom UIView inside a UIScrollView. And in the custom view, I could keep drawing things. But, I ended up with a warning saying something like below and nothing drawn on screen. [<CALayer: 0x5584190> display]: Ignoring bogus layer size (50000, 50000) Which means, I can't have such a big CALayer to draw things. Now, solution? alternative? Then comes CATiledLayer. I made my large UIView backed by CATiledLayer now. After having a proper levelOfDetails and levelOfDetailsBias value, things worked like charm. Until I ended up facing another problem. Since, CATiledLayer caches drawing in different zoom levels if I try to scale the view after changing the drawing content the cached drawings appear and then the new contents get drawn. I don't find an option to invalidate caches in different levels. All the solutions I came across leads me to clear the entire contents of the CATiledLayer on drawing content change which won't help again. Do I miss something here? Is there a way with which I can clear caches at different levels? Or is there any other solutions which could solve my need? Can someone help me with this?
1
0
1.4k
Aug ’23
Metal + UIKit Timing Issues
Hi! I am currently finalizing a new app that uses Metal to render a 3D scene and a UIKit overlay to display controls for interacting with objects in the scene. The render loop is driven via a CADisplayLink with its preferredFramesPerSecond set to 60. I have recently noticed an issue where the app reports a steady 60 fps frame rate in the Xcode debug navigator, but still felt sluggish on the device. This feeling was only present on devices with ProMotion and often started after interactions with the UIKit overlay. I started investigating by using Metal System Trace and quickly found an explanation for the sluggish feeling: occasionally, the app would switch from its nominal 16ms-16ms-16ms cadence to 12ms-20ms-12ms, thus still averaging 60 fps, but with inconsistent frame times. Pictures of the timeline can be found here. I have tried setting the CAMetalLayer's presentsWithTransaction to true, waiting for the command buffer to be scheduled and then presenting the drawable, but, unfortunately, the problem persists. If anybody can think of a potential reason / solution for this, I would be very thankful.
5
0
2.4k
Aug ’23
Change MapKit region update animation duration on macOS
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
0
0
718
Aug ’23
CARemoteLayer Refusing To Draw Hosted Contents
I'm exploring using the CARemoteLayerClient/Server API to render a layer from another process as is described in the docs, but can't seem to get a very simple example to work. Here's a very simple example of what I'd expect to work: // Run with `swift file.swift` import AppKit let app = NSApplication.shared class AppDelegate: NSObject, NSApplicationDelegate { let window = NSWindow( contentRect: NSMakeRect(200, 200, 400, 200), styleMask: [.titled, .closable, .miniaturizable, .resizable], backing: .buffered, defer: false, screen: nil ) func applicationDidFinishLaunching(_ notification: Notification) { window.makeKeyAndOrderFront(nil) let view = NSView() view.frame = NSRect(x: 0, y: 0, width: 150, height: 150) view.layerUsesCoreImageFilters = true view.wantsLayer = true let server = CARemoteLayerServer.shared() let client = CARemoteLayerClient(serverPort: server.serverPort) print(client.clientId) client.layer = CALayer() client.layer?.backgroundColor = NSColor.red.cgColor // Expect red rectangle client.layer?.bounds = CGRect(x: 0, y: 0, width: 100, height: 100) let serverLayer = CALayer(remoteClientId: client.clientId) serverLayer.bounds = CGRect(x: 0, y: 0, width: 100, height: 100) view.layer?.addSublayer(serverLayer) view.layer?.backgroundColor = NSColor.blue.cgColor // Background blue to confirm parent layer exists window.contentView?.addSubview(view) } } let delegate = AppDelegate() app.delegate = delegate app.run() In this example I'd expect there to be a red rectangle appearing as the remote layer. If I inspect the server's layer hierarchy I see the correct CALayerHost with the correct client ID being created, but it doesn't display the correct contents being set from the client side. After investigating this thread: https://bugs.chromium.org/p/chromium/issues/detail?id=312462 and some demo projects, I've found that the workarounds previously found to make this API work no longer seem to work on my machine (M1 Pro, Ventura). Am I missing something glaringly obvious in my simple implementation or is this a bug?
0
0
568
Jul ’23
Animation and position
Hi I'm trying to resize my NSPanel with spring animation but I have a position problem (the panel is not on center of screen) when I make the panel bigger (see the video) How can I fix it? Video: https://streamable.com/l4281l func animationBigger () { let jump = CASpringAnimation(keyPath: "transform.scale") jump.damping = 60 jump.mass = 10 jump.initialVelocity = 10 jump.stiffness = 200.0 jump.fromValue = 1.0 jump.toValue = 2.0 jump.duration = 0.4 //self is NSPanel self.contentView?.layer!.add(jump, forKey: nil) self.setContentSize(bignotchSize) } class NotchView: NSView { private var mouseDownSubject = PassthroughSubject<NSPoint, Never>() var mouseDownPublisher: AnyPublisher<NSPoint, Never> { return mouseDownSubject.eraseToAnyPublisher() } //Draw notch override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) drawNotch() } func drawNotch () { self.layer?.anchorPoint = CGPoint(x: 0.0, y: 0.0) let size = self.frame.size let r: CGFloat = 15.0 let gap: CGFloat = 1.0 let notch = NSBezierPath() notch.move(to: NSPoint(x: 0.0, y: size.height)) notch.curve(to: NSPoint(x: r, y: size.height - r), controlPoint1: NSPoint(x: r - gap, y: size.height), controlPoint2: NSPoint(x: r, y: size.height - gap)) notch.line(to: NSPoint(x: r, y: r)) notch.curve(to: NSPoint(x: 2 * r, y: 0.0), controlPoint1: NSPoint(x: r, y: gap), controlPoint2: NSPoint(x: r + gap, y: 0.0)) notch.line(to: NSPoint(x: size.width - 2 * r, y: 0.0)) notch.curve(to: NSPoint(x: size.width - r, y: r), controlPoint1: NSPoint(x: size.width - r - gap, y: 0.0), controlPoint2: NSPoint(x: size.width - r, y: gap)) notch.line(to: NSPoint(x: size.width - r, y: size.height - r)) notch.curve(to: NSPoint(x: size.width, y: size.height), controlPoint1: NSPoint(x: size.width - r, y: size.height - gap), controlPoint2: NSPoint(x: size.width - r + gap, y: size.height)) notch.close() NSColor.black.setFill() //change to black to see the notch notch.fill() }``` ```language class NotchPanel: NSPanel, CAAnimationDelegate { private (set) var notchView = NotchView() var notchClickPublisher: AnyPublisher<NSPoint, Never> { return notchView.mouseDownPublisher } init(_ center: NSPoint,_ size: NSSize) { let notchFrame = NSRect(x: center.x - 0.5 * size.width, y: center.y - size.height, width: size.width, height: size.height) super.init(contentRect: notchFrame, styleMask: [ .nonactivatingPanel,.nonactivatingPanel, .hudWindow], backing: .buffered, defer: true) self.level = .popUpMenu self.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] self.isOpaque = true self.isMovable = false //Disable notch to be moveable self.hasShadow = false //we do custom shadow later self.backgroundColor = NSColor.systemPink //notch background setNotchView() } private func setNotchView() { self.contentView?.addSubview(notchView) notchView.translatesAutoresizingMaskIntoConstraints = false notchView.leftAnchor .constraint(equalTo: self.contentView!.leftAnchor) .isActive = true notchView.topAnchor .constraint(equalTo: self.contentView!.topAnchor) .isActive = true notchView.rightAnchor .constraint(equalTo: self.contentView!.rightAnchor) .isActive = true notchView.bottomAnchor .constraint(equalTo: self.contentView!.bottomAnchor) .isActive = true }```
0
0
581
Jun ’23
CABasicAnimation change size of rect
Hi, I'm trying to resize my rect with CABasicAnimation and the animation go to up and not down How can I change it? Video: https://streamable.com/nw4ekc Thank you! let newFrame = NSRect(x: blueRect.frame.origin.x, y: blueRect.frame.origin.y - 100,width: blueRect.frame.size.width, height: 200) let animation = CABasicAnimation(keyPath: "bounds") animation.fromValue = NSValue(rect: blueRect.frame) animation.toValue = NSValue(rect: newFrame) animation.duration = 0.5 // Animation duration in seconds blueRect.layer?.add(animation, forKey: "sizeAnimation") // Update the rect view's size and origin blueRect.frame = newFrame
1
0
879
Jun ’23