Post

Replies

Boosts

Views

Activity

UIImagePickerController in iOS 13: Did apple abandon navigation bar customizability?
In iOS 13 some things seem to have changed in terms of customizing the navigation bar appearance. I want to customize the navigation bar of a UIImagePickerController. According to iOS 13 guidelines the code to change an existing navigation bar (in this case changing the UIImagePickerController navigation bar background color to red) would be:let imagePickerViewController = UIImagePickerController() let barAppearance = UINavigationBarAppearance() barAppearance.configureWithDefaultBackground() barAppearance.backgroundColor = .red imagePickerViewController.navigationBar.standardAppearance = barAppearance imagePickerViewController.navigationBar.scrollEdgeAppearance = barAppearanceUnfortunately, this does not work. In general setting imagePickerViewController.navigationBar appearance does not have any influence on the presented UIImagePickerController.The only thing that seems to be working is changing the default appearance of all UINavigationBars like this:UINavigationBar.appearance().tintColor = .redBut this does not allow individual customization and does not allow for extending the navigation bar with your own UIBarButtonItems and other navigation bar elements.I have seen discussion about this topic, but none of those discussions has a working answer and none of them discusses if apple has made any changes in terms of general customizability of the UIImagePickerController. So here are my questions:Did apple remove the option of customizing the navigation bar of an UIImagePickerController in iOS 13?If not, how do you actually customize the navigation bar of an UIImagePickerController in iOS 13?
2
0
2.6k
Jun ’20
CALayer setNeedsDisplay "accumulation" problem
It is my understanding that CALayer.setNeedsDisplay registers the CALayer for redisplaying its content, which eventually leads to a call to CALayer.display. Multiple calls to CALayer.setNeedsDisplay in a short amount of time (lower than display refresh cycle) should "accumulate" to approximately trigger one single call of CALayer.display. The following code in fact leads to only one call of CALayer.display (full code at the end of post): for _ in 0..<200 { &#9;&#9;let timeInterval: TimeInterval = Double(arc4random_uniform(1000)) / 40000.0 // random value between 0 and 25 ms &#9;&#9;Thread.sleep(forTimeInterval: timeInterval) &#9;&#9;testLayer.setNeedsDisplay() } Unfortunately calling CALayer.setNeedsDisplay multiple times in this short amount of time (lower than display refresh cycle) from different threads (but still making sure it is called on the main thread since CALayer.setNeedsDisplay needs to be called from the main thread) seems to trigger CALayer.display multiple times instead of only one single time. Here is a small code excerpt how this would look like (full code at the end of post): for _ in 0..<200 { &#9;&#9;DispatchQueue.global(qos: .userInteractive).async { &#9;&#9;&#9;&#9;let timeInterval: TimeInterval = Double(arc4random_uniform(1000)) / 40000.0 // random value between 0 and 25 ms &#9;&#9;&#9;&#9;Thread.sleep(forTimeInterval: timeInterval) &#9;&#9;&#9;&#9;DispatchQueue.main.async { &#9;&#9;&#9;&#9;&#9;&#9;testLayer.setNeedsDisplay() &#9;&#9;&#9;&#9;} &#9;&#9;} } In my tests the code above lead to the unexpected described behavior. Tested in XCode 12.0.1 using XCode Playground. I also tested a similar code in an app running on my IPad Pro (first generation) showing the same behavior. My questions are: How can I make sure CALayer.display is called only once in the second case instead of multiple times? Why does this happen? Full Playground code, which shows the bahavior described above (the output shows that testLayer.display is called with much higher frequency than any reasonable display refresh rate): import Foundation import UIKit import PlaygroundSupport /// class for conveniently measuring timespans class TimeTicToc { &#9;&#9;static var lastTime: TimeInterval? &#9;&#9; &#9;&#9;@discardableResult static func timeSinceLastCall(_ functionName: String? = nil) -> TimeInterval? { &#9;&#9;&#9;&#9;let currentTime: TimeInterval = Date().timeIntervalSince1970 &#9;&#9;&#9;&#9;var timeInterval: TimeInterval? &#9;&#9;&#9;&#9;if let lastCallTime = TimeTicToc.lastTime { &#9;&#9;&#9;&#9;&#9;&#9;let timeDiff = (currentTime - lastCallTime) * 1000.0 &#9;&#9;&#9;&#9;&#9;&#9;if let functionName = functionName { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;print("time since last call of \(functionName) = \(timeDiff) (ms), which corresponds to \(1000.0 / timeDiff) Hz") &#9;&#9;&#9;&#9;&#9;&#9;} else { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;print("time since last call = \(timeDiff), which corresponds to \(1000.0 / timeDiff) Hz") &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;timeInterval = timeDiff &#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;TimeTicToc.lastTime = currentTime &#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;return timeInterval &#9;&#9;} } /// class derived from CALayer to expose display function class TestLayer: CALayer { &#9;&#9;override func display() { &#9;&#9;&#9;&#9;super.display() &#9;&#9;&#9;&#9;TimeTicToc.timeSinceLastCall("display") &#9;&#9;} } // setup liveView let liveView = UIView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 200.0, height: 200.0))) liveView.backgroundColor = UIColor.red // setup testLayer as sublayer of liveView let testLayer = TestLayer() testLayer.frame = CGRect(origin: CGPoint.zero, size: 0.5 * liveView.frame.size) testLayer.backgroundColor = UIColor.green.cgColor liveView.layer.addSublayer(testLayer) PlaygroundPage.current.liveView = liveView // execute setNeedsDisplay, enqueued from multiple threads with different very short delays (smaller than display refresh cicle) for _ in 0..<200 { &#9;&#9;DispatchQueue.global(qos: .userInteractive).async { &#9;&#9;&#9;&#9;let timeInterval: TimeInterval = Double(arc4random_uniform(1000)) / 40000.0 // random value between 0 and 25 ms &#9;&#9;&#9;&#9;Thread.sleep(forTimeInterval: timeInterval) &#9;&#9;&#9;&#9;DispatchQueue.main.async { &#9;&#9;&#9;&#9;&#9;&#9;testLayer.setNeedsDisplay() &#9;&#9;&#9;&#9;} &#9;&#9;} }
0
0
899
Sep ’20
How to customize UIDocumentPickerViewController?
Is there any way to customize UIDocumentPickerViewController in terms of its visual appearance and additional actions for its menu? I've seen some of these customization options for UIDocumentBrowserViewController, but none of them seems to be listed for UIDocumentPickerViewController in Apple's developer documentation. Any hints are appreciated.
0
0
499
Feb ’22
UIImageView: change resolution of displayed vector image (for zooming)
Hi, I have the following simple view hierarchy, which is basically a scrollView with its contentView, which contains an imageView: This imageView has a vector image assigned to its image property. The problem now is that when zooming with the scrollView the resolution of the image - the imageView displays - stays the same and the image is displayed blurry. This is of course expected behavior. My question now is: what is the proper way to adjust the resolution of the displayed image in the imageView to scrollView.zoomScale? I have a bunch of "workarounds" like setting a bigger frame for imageView and then scaling it down via its transform property, but those don't seem like the proper and intended way to do this. Any suggestions?
0
0
509
Sep ’22
UIManagedDocument: How to store additional data like a Thumbnail in an iCloud compatbile way?
Hi, I will be honest with you, I am quite confused on how to store additional content the right way using UIMangedDocument. Unfortunately the documentation on UIMangedDocument seems to be lacking a lot of information. I am porting my UIDocument based app to a UIManagedDocument based app in order to make use of Core Data. In my UIDocument based approach I just stored additional data via overriding contents(forType:) to return a FileWrapper, which contains the appropriate structure and data like a Thumbnail. Now it is my understanding, that in order to store additional data with UIManagedDocument you could override additionalContent(for:) which needs to return a Dictionary, not a FileWrapper. I want this to work with iCloud and Apple mentions in the documentation for additionalContent(for:): "Additional content isn’t supported on iCloud." So this does not seem to be the right approach for me. I could of course store any additional data directly in Core Data's managed object context. But especially in the case of a thumbnail I think this is not the right approach because I would have to create a new UIMangedDocument, open it and then fetch the thumbnail from the persistent store every single time I just want to show a thumbnail for the document. Also using thumbnailDictionaryKey and fileAttributesToWrite(to:for:) is not the right way as it is deprecated and I want to use the proper new way of a thumbnail extension for showing thumbnails of the documents. So, what is the right way to store additional data like thumbnails in a UIManagedDocument in a way that is compatible with iCloud?
0
0
410
Jun ’23
(CoreData) Using UndoManger on a private Managed Object Context
I am having trouble using UndoManager with a private Manged Object Context in CoreData. Here is my setup: I have my main context running on the main queue. I created a private context via let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) and connect it as a child context to my main context via privateContext.parent = mainContext. I also assigned an UndoManager to privateContext. This UndoManger has been set to use manual grouping via undoManager.groupsByEvent = false. So far so good. Now to the part that gives me trouble: I want to change something on the private context and record this action for potential undoing. In order to do this a use a nested grouping structure (that is how it makes sense in my code). This is how i currently do this: privateContext.performAndWait { undoManger.beginUndoGrouping } // do something in code unrelated to changes on the privateContext privateContext.performAndWait { doChangesOnPrivateContext() } privateContext.performAndWait { undoManger.beginUndoGrouping } privateContext.performAndWait { doChangesOnPrivateContext() } privateContext.performAndWait { undoManger.endUndoGrouping } // do something in code unrelated to changes on the privateContext privateContext.performAndWait { undoManger.endUndoGrouping } This leads to the error: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '_setGroupIdentifier:: NSUndoManager is in invalid state, must begin a group before registering undo which I had encountered before when beginUndoGrouping and endUndoGrouping were called from different threads. Further examination yields that this is also the case here as performAndWait runs the code on the private queue assigned to privateContext, but with the structure shown above beginUndoGrouping and endUndoGrouping are indeed called on different threads. So here are my questions: How do I do this correctly? Do I misunderstand things and UndoMangager should not be used on a private context? If so, how would I setup things then?
0
0
425
Aug ’23