Posts

Post not yet marked as solved
0 Replies
131 Views
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.
Posted
by 3DWizard.
Last updated
.
Post not yet marked as solved
0 Replies
388 Views
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;} }
Posted
by 3DWizard.
Last updated
.
Post not yet marked as solved
2 Replies
1.6k Views
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?
Posted
by 3DWizard.
Last updated
.
Post not yet marked as solved
21 Replies
6.1k Views
After updating to Xcode 10.0 I realized that the draw(_ rect: CGRect) routine of my custom UIView (class derived from UIView) in my application was called with the wrong rect. Indeed it is always called with rect being the full frame of the underlying UIView, instead of the rect being specified by setNeedsDisplay(_ rect: CGRect).Here is a code snippet that can be run as a playground, which at least in my setup shows the erroneous behavior described above in a minimalistic setting:import Foundation import UIKit import PlaygroundSupport class CustomView: UIView { override func draw(_ rect: CGRect) { print("rect = \(rect)") } } let customView = CustomView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 200.0, height: 200.0))) PlaygroundPage.current.liveView = customView print("test") customView.setNeedsDisplay(CGRect(origin: CGPoint.zero, size: CGSize(width: 100.0, height: 100.0)))The output I get isrect = (0.0, 0.0, 200.0, 200.0) test rect = (0.0, 0.0, 200.0, 200.0)The first printed output for rect is the standard full redraw of the view, but the second one after printing "test" shows the problem. This second output is from redrawing due to the customView.setNeedsDisplay call and should be the smaller specified rectangle (0.0, 0.0, 100.0, 100.0).So my obvious questions are:Can you reproduce this behavior?Am I missing something obvious?Is this a bug?
Posted
by 3DWizard.
Last updated
.