I'm developing an iOS 14 Catalyst app and I'm trying to setup the window toolbar.
I created a NSToolbar and assigned to the scene window titlebar property.
var toolbarDelegate = ToolbarDelegate()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
#if targetEnvironment(macCatalyst)
guard let windowScene = scene as? UIWindowScene else { return }
let toolbar = NSToolbar(identifier: "main")
toolbar.delegate = toolbarDelegate
toolbar.displayMode = .iconOnly
if let titlebar = windowScene.titlebar {
titlebar.toolbar = toolbar
titlebar.toolbarStyle = .unified
titlebar.titleVisibility = .hidden
}
#endif
}
I then assigned some items to the toolbar via the toolbarDefaultItemIdentifiers delegate method.
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
let identifiers: [NSToolbarItem.Identifier] = [
.toggleSidebar,
.print,
.flexibleSpace,
.print
]
return identifiers
}
This work as expected.
Now, let's say that I want to align some items with the edges of the supplementary column.
I found that there is an NSToolbarItem named supplementarySidebarTrackingSeparatorItemIdentifier.
This item appears to allow us to align items with the supplementary column. If I do this:
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
let identifiers: [NSToolbarItem.Identifier] = [
.toggleSidebar,
.flexibleSpace,
.print,
.supplementarySidebarTrackingSeparatorItemIdentifier,
.print,
.flexibleSpace,
.print
]
return identifiers
}
I got the following result which is exactly what I want to achieve (the items are aligned with the supplementary column):
But there are some issues.
As you can see from the above image for some reason the background color of the toolbar on top of the supplementary column become white. But it's worse. If I resize the window the background instantly become gray:
If I then scroll the content of the supplementary column the toolbar become white again.
Another issue is that If I collapse the primary column the toolbar on top of the supplementary column loose the right separator line. Why?
I tried to search some info online but if I try to search for supplementarySidebarTrackingSeparatorItemIdentifier I got only 5 results! One of these is Apple's official documentation page, which does not contain any info about the behaviour of this item:
Apple documentation about supplementarySidebarTrackingSeparatorItemIdentifier
At this point I wonder if this item is ready to be used in real apps.
Someone has experience using the supplementarySidebarTrackingSeparatorItemIdentifier item?
There is a way to align toolbar items with the supplementary column without having the above described issues? (different toolbar background color, missing toolbar separator line)
Thank you
Post
Replies
Boosts
Views
Activity
If we use white as tint color for an inline or compact UIDatePicker (iOS 14, dark mode) then if the today day is selected the number become unreadable.
The problem is that the today day number color is always white and, when selected, the background color is also white.
Anyone know a way to set the color of today's day number to black when is selected?
Thank you
I'm using the following code to get the height of the keyboard:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardHeight),
name: UIResponder.keyboardDidChangeFrameNotification,
object: nil
)
}
@objc private func keyboardHeight(notification: Notification) {
guard let keyboardRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
print("==> keyboardFrameEndUserInfoKey height: \(keyboardRect.size.height)")
}
}
This code on iPad work correctly when the keyboard is normal but it does not work when the keyboard is floating.
After the user tap on a text field, the keyboard appear and the keyboardDidChangeFrameNotification notification is called.
If the keyboard is floating then the first time we get a wrong height of 362. If the user then manually move somewhere the floating keyboard the notification is called again and the correct value of 308 is returned.
It is a bug or I miss something? I need to be able to get the correct height the first time the keyboard appear.
This is happening on iOS 13 and iOS 14.
Any idea?
Hi,
Sometimes it is useful to show a movie to better explain the behaviour of something. Animated gifs would be perfect for this purpose.
Unfortunately it doesn't seem possible to include animated gifs in posts. When I try to do it (using the "Add Image" feature) I always got this error message:
Will the attachment of animated gifs be supported in the future? If not, what is the recommended way to include a short movie in a post?
Thank you
Hi,
On iOS 13 and iOS 14, if you have a navigation controller that use large titles and you push a controller that does not want a large title (navigationItem.largeTitleDisplayMode = .never) there is a smooth animation between the two navigation bar heights (from the large one to the small one).
On iOS 15 the animation is missing and during the transition the pushed controller view top part is covered by the large title bar until the end of the transition. After the animation is done the height of the navigation bar suddenly becomes small.
Here's the transition on iOS 14. The navigation bar height gradually changes from large to small:
And here's the transition on iOS 15. The navigation bar height is not animated and remains the same until the end of the transition animation:
I opened a bug report regarding this issue (FB9290717) but I want to know if someone has found a temporary fix for this.
Thank you
Hi,
I want to present four view one after the other.
The user should be able to move between views by clicking on a NavigationLink inside the navigation bar.
Below you can find the sample code.
Unfortunately I struggle to get it to work properly.
When the user tap on "To second" the second view is correctly pushed into the navigation stack.
But when the user press on "To third" or "To fourth" something strange happens.
There is no transition animation and the back button is not updated.
If the user tries to return to the previous view by pressing the back button it will be sent to the first view.
There is something I'm doing wrong?
Here is the code:
struct FirstView: View {
		var body: some View {
				NavigationView {
						Text("First view\n\nIf you press on \"To second\" the second view is correctly presented.")
								.navigationBarTitle(Text("First view"), displayMode: .inline)
								.navigationBarItems(trailing:
										NavigationLink("To second", destination: SecondView())
								)
				}
		}
}
struct SecondView: View {
		var body: some View {
				Text("Second view\n\nIf you press on \"To third\" the view is presented without animation.")
						.navigationBarTitle(Text("Second view"), displayMode: .inline)
						.navigationBarItems(trailing:
								NavigationLink("To third", destination: ThirdView())
						)
		}
}
struct ThirdView: View {
		var body: some View {
				Text("Third view\n\nIf you press on \"To fourth\" the view is presented without animation.\n\nThe back button is not updated and if you press on it you are not redirected to \"Second view\" but to \"First view\".")
						.navigationBarTitle(Text("Third view"), displayMode: .inline)
						.navigationBarItems(trailing:
								NavigationLink("To fourth", destination: FourthView())
						)
		}
}
struct FourthView: View {
		var body: some View {
				Text("Fourth view\n\nThe back button is not updated and if you press on it you are not redirected to \"Third view\" but to \"First view\".")
						.navigationBarTitle(Text("Fourth view"), displayMode: .inline)
		}
}
Hi,
I found a case that cause a list to never deselect the row when the back button is pressed.
In the following code I modally present a list. When you select a row another list is displayed. If you select a row of the second list and you go back the row will stay selected.
This is the code:
struct ContentView: View {
		@State var modalVisible = false
		var body: some View {
				Button(action:{
						self.modalVisible.toggle()
				}) {
						Text("Open modal")
				}
				.sheet(isPresented: $modalVisible) {
						NavigationView {
								List {
										NavigationLink("Item", destination: List {
												NavigationLink("Sub Item", destination: Text("If you go back the row will stay selected."))
										})
}
}
}
}
}
Does anyone know if there is a way to force the list to deselect the row when the back button is pressed?
Thank you
Hi,
I want to set the style of a DatePicker to inline but I can't figure out how to do it.
My DatePicker is defined like this:
DatePicker("Time",
	 selection: $time,
	 displayedComponents: [.hourAndMinute])
	 .pickerStyle(InlinePickerStyle())
Unfortunately the picker is not really displayed inline because when the user tap on it the picker is presented modally over a blurred background.
I tried to change the available picker styles but the result is always the same.
I want to achieve what Apple did in the Remainders App. Here when you activate the "Date" or "Time" switch a date picker is displayed inline.
Can anyone help me?
Thank you
Hi,
Sorry for the long post but I have an issue with SwiftUI that is driving me crazy.
Let's take this example view:
struct TestNumberPicker: View {
	 @State private var number = UserDefaults.standard.integer(forKey: "number")
	 var body: some View {
			VStack {
				 Stepper("Number", value: $number, in: 0...240, step: 5)
				 Text("Selected number: \(number)")
			}
			.onChange(of: number, perform: { newNumber in
				 UserDefaults.standard.setValue(newNumber, forKey: "number")
			})
	 }
}
The view has a state variable named "number" initialized with a value readed from the UserDefaults. This variable is linked to a Stepper. When the value of "number" is changed a block is executed to save the new value in the UserDefaults.
Now, this works correctly if for example this view is presented modally but it doesn't work anymore if the view is presented via a NavigationLink, like this:
struct TestForm: View {
	 var body: some View {
			NavigationView {
				 Form {
						NavigationLink("Pick a number", destination: TestNumberPicker())
				 }
				 .navigationBarTitle(Text("Test"), displayMode: .inline)
			}
	 }
}
The first time the view is displayed everything work as expected but if I tap on back and I select again the NavigationLink something weird appends. The "number" variable is reset to the original value.
This is an example: I tap on the navigation item.
The view is displayed.
The "number" variable is initialised with 10 (read from the UserDefaults).
I tap once on the stepper (+).
The "number" variable become 15.
The view is displaying "Selected number: 15"
The UserDefaults save 15 (confirmed by reading the plist file).
I tab on back.
I tap again on the NavigationItem.
The view is displayed but the "number" variable contain 10 instead of 15!
I believe the problem has to do with NavigationLink not lazy loading the view. In fact if I forces the view to load lazy:
struct TestForm: View {
	 struct LazyView<Content: View>: View {
			let build: () -> Content
			init(_ build: @autoclosure @escaping () -> Content) {
				 self.build = build
			}
			var body: Content {
				 build()
			}
	 }
	 var body: some View {
			NavigationView {
				 Form {
						NavigationLink("Pick a number", destination: LazyView(TestNumberPicker()))
				 }
				 .navigationBarTitle(Text("Test"), displayMode: .inline)
			}
	 }
then everything work as expected.
What is exactly the problem?
It is something I'm doing wrong?
Thank you
Hi,
I'm learning SwiftUI and there are still some things I don't understand very well.
For example, this is compiling correctly:
struct Test: View {
private var testPrivateProperty = 1
var body: some View {
Text("Test")
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}
This is also compiling correctly:
struct Test: View {
@Binding var testBinding: Bool
var body: some View {
Text("Test")
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test(testBinding: .constant(true))
}
}
But if I create a View that contain both a binding and a private property:
struct Test: View {
@Binding var testBinding: Bool
		private var testPrivateProperty = 1
var body: some View {
Text("Test")
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test(testBinding: .constant(true))
				//Error on the line above.
}
}
the compiler produce this error:
'Test' initializer is inaccessible due to 'private' protection level Can anyone help me understand why?
Thank you
Hi,
I'm testing one of my app on iOS 14 with Xcode 12 beta 3 (12A8169g) and I have a problem with my storyboards.
Xcode give me this error for all the storyboards that contain a split view controller:
An internal error occurred. Editing functionality may be limited.
The log generated by the Xcode "Report a bug" button say:
Exception name: NSInvalidArgumentException
Exception reason: UITabBarController is unsupported as viewController for -[UISplitViewController setViewController:forColumn:] in Primary column
It worked correctly on Xcode 12 beta 2.
Has anyone encountered the same problem and found a way to fix it?
Thank you
Hi!
On iOS 14 a UISplitViewController inside my app has stopped calling some of its delegate methods.
For example on iOS 13 the delegate method collapseSecondaryOntoPrimaryViewController is correctly called when needed.
On iOS 14 this method is not called and this message is printed on the output:
[UISplitViewController] Skipping delegate calllback, splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:. Unsupported for UISplitViewController style DoubleColumn
Can someone help me?
I'm using Xcode 12 beta 3 (12A8169g).
Thank you
Alan
Hi!I have the "-com.apple.CoreData.ConcurrencyDebug 1" argument set in the schema to check if I have some CoreData concurrency problems.I don't know why but this code give me an Multithreading_Violation_AllThatIsLeftToUsIsHonor error.Do you think I did something wrong with this code?private func loadSigns(searchTerm: String) {
let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateMOC.parent = managedObjectContext
let fetchRequest: NSFetchRequest = Sign.fetchRequest()
let predicate = NSPredicate(format: "name CONTAINS[cd] %@", searchTerm, 0)
fetchRequest.predicate = predicate
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) {(result:NSAsynchronousFetchResult!) -> Void in
print("request done")
}
do {
_ = try privateMOC.execute(asynchronousFetchRequest) as? NSPersistentStoreAsynchronousResult
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}I also tried to incapsulate the code inside the perform method of the privateMOC but I still have the Multithreading_Violation_AllThatIsLeftToUsIsHonor error:private func loadSigns(searchTerm: String) {
let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateMOC.parent = managedObjectContext
privateMOC.perform {
let fetchRequest: NSFetchRequest = Sign.fetchRequest()
let predicate = NSPredicate(format: "name CONTAINS[cd] %@", searchTerm, 0)
fetchRequest.predicate = predicate
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) {(result:NSAsynchronousFetchResult!) -> Void in
print("request done")
}
do {
_ = try privateMOC.execute(asynchronousFetchRequest) as? NSPersistentStoreAsynchronousResult
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}Any idea?Thank you
Hi!I found a situation where a split view controller on iOS 13 never let the controller displayed in the detail to dealloc.The problem is reproducible with the standard Xcode “Master-Detail App” project template.This is how you can reproduce the problem:In Xcode create a new “Master-Detail App” project.In “DetailViewController” add a deinit method and print something.Run the project on a device that allows you to switch from a compact split view to an expanded split view (for example the iPhone 8 Plus).Set the device to portrait.If you choose an item on the master list the detail is loaded and you will see that the previously controller that was present on the detail is deallocated. That’s correct.Now rotate the device to landscape to expand the split view.Choose an item in the master. The controller will be displayed on the detail.Rotate the device to portrait. The split view will collapse.Tap on the back arrow (that for some strange reason on iOS 13 will no more display the title but only the arrow icon, but that’s another problem).Choose another item on the master.The previous controller will never be deallocated and will stay in memory forever.If you try the same thing on iOS 12 you will see that everything is working as expected.Had any of you already noticed this? If yes, have you found a way to fix the problem?Thank youAlan
Hi!In one of my app I need to control the color of the status bar (need to be white).For doing this I have added the UIViewControllerBasedStatusBarAppearance = true in Info.plist and overrided the preferredStatusBarStyle property of the view controllers / navigation controllers to lightContent.Everything works properly except one thing. If I request a biometric authentication via LAContext.evaluatePolicy when the Touch ID dialog is displayed the status bar color change to black.You can see the behaviour in the below image. Before the Touch ID message is displayed the status bar is white but when the message appear it become black.https://www.dale1.ch/documents/status-bar-authentication-dialog.pngSomeone know how to keep the status bar white even when the Touch ID request message is displayed?Unfortunately I cannot set the UIViewControllerBasedStatusBarAppearance attribute to false.Thank youAlan