SwiftUI ToolbarItemGroup for Keyboard on iOS16 - swiftui

Due to the new NavigationStack having quite a few problems, including a pretty significant memory leak at this time, I am still using the old NavigationView with a build target of iOS 15+.
However, devices running iOS 16 are no longer showing the ToolbarItemGroup contents in the keyboard. ToolbarItem(placement: .navigationBarTrailing) works perfectly, but ToolbarItemGroup(placement: .keyboard) no longer shows the contents.
Here's the code I am using for the keyboard toolbar items:
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
HStack {
Spacer()
Button {
isFocused = nil
} label: {
Image(systemName: "keyboard.chevron.compact.down")
}
}
}
}
This should show the image at the top of the keyboard, allowing the user to unfocus on tap. It works perfectly on iOS 15 but nothing shows on a device running iOS 16.
I am testing on a real device, not the simulator.

This seems to be yet another iOS 16 bug. It appears that if you wrap your entire keyboard toolbar in an HStack it will not appear in iOS 16, however, if you imagine that an HStack is implicitly surrounding all of your keyboard toolbar elements in the first place, and subsequently remove the HStack wrap, everything works as it should.
Additionally, as long as your entire keyboard toolbar is NOT wrapped in an HStack, you may have sub-HStack elements in your toolbar.
Here's the solution:
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button {
isFocused = nil
} label: {
Image(systemName: "keyboard.chevron.compact.down")
}
}
}

The following worked for me on iOS 16.2
I had exactly the same issue. Unfortunately, #kittonian's solution did not work for me, but the following did: keeping the HStack, but adding an empty Text element on its left.
.toolbar {
ToolbarItem(placement: .keyboard) {
HStack {
Text("")
Spacer()
Button("Done") {
field = nil
}
}
}
}

Related

SwiftUI navigationBarTitle not resetting to .large returning from Toolbar set to .inline

There are a few posts regarding SwiftUI .inline not resetting to .largeTitle when navigation returns to the parent:
For example:
Navigation bar title stays inline in iOS 15
and
Navigationbar title is inline on pushed view, but was set to large
While earlier posts seem to suggest this has been corrected, I'm running into the same problem, even in iOS 16, but I'm not using a < Back button, instead I'm using "Cancel" (and not show, "Save") on my DestinationView. My goal is to mimic Apple's practice of showing a modal view when adding data, but a show-style push on the navigation stack when viewing and editing existing data (e.g. Contacts app, Reminders app, Calendar app). The brief code below illustrates the problem without adding extra code to handle data updating (e.g. #EnviornmentObject).
When I run this in the Live Preview in Xcode 14.0.1, scheme set to iPhone 13 Pro, no problems. Click a NavLink, return from destination, and ContentView shows .large navigationBarTitle. BUT when I run in the simulator or on a 13 Pro device, returning to Home from a NavigationLink remains .inline unless I pull down on the list. If I switch to iPhone 14 Pro, the live preview looks fine, but the simulator shows a short of abrupt switch from inline back to large, not a smooth animation. Am I doing something wrong in the setup here or is there a bug in the implementation, noting that the behavior oddly holds to .inline on return home to ContentView, if I use this in either a simulator or device for iPhone 13 Pro. Thanks for guidance & insight!
struct ContentView: View {
#State private var sheetIsPresented = false
var items = ["Item1", "Item2", "Item3"]
var body: some View {
NavigationStack {
List {
ForEach(items, id: \.self) { item in
NavigationLink(item, destination: DestinationView(item: item))
.padding()
}
}
.navigationBarTitle("Home", displayMode: .large)
.listStyle(.plain)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
sheetIsPresented.toggle()
} label: {
Image(systemName: "plus")
}
}
}
}
.sheet(isPresented: $sheetIsPresented) {
NavigationStack {
DestinationView(item: "New!")
}
}
}
}
struct DestinationView: View {
var item: String
#Environment(.dismiss) private var dismiss
var body: some View {
List {
Text(item)
}
.toolbar {
ToolbarItem (placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
}
}
}
.listStyle(.plain)
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden()
}
}

GeometryReader acting weird when presenting a modal on iOS 16. Bug or new behavior?

I'm seeing a weird behavior that is affecting one of my views in SwiftUI after upgrading to iOS 16.
Just to give some context, here is the stack:
Xcode 14
Simulator or real device on iOS 15.5 and 16
Considering the minimum reproducible code below:
struct ContentView: View {
#State private var isPresented: Bool = false
var body: some View {
GeometryReader { reader in
VStack(spacing: 36) {
Text("Screen frame:\n\(String(describing: reader.frame(in: .global)))")
.multilineTextAlignment(.center)
Button {
isPresented.toggle()
} label: {
Text("Open modal")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
.onReceive(NotificationCenter.default.appDidBecomeActive()) { _ in
print(reader.frame(in: .global))
}
.onReceive(NotificationCenter.default.appDidEnterBackground()) { _ in
print(reader.frame(in: .global))
}
}
.sheet(isPresented: $isPresented) {
modalView
}
}
private var modalView: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.padding()
}
}
extension NotificationCenter {
func appDidBecomeActive() -> AnyPublisher<Notification, Never> {
publisher(for: UIApplication.didBecomeActiveNotification).eraseToAnyPublisher()
}
func appDidEnterBackground() -> AnyPublisher<Notification, Never> {
publisher(for: UIApplication.didEnterBackgroundNotification).eraseToAnyPublisher()
}
}
As soon the view starts, it's possible to see the frame available due to the GeometryReader. Then following the steps:
Open the modal view
Send the app to the background
Open the app again
Close the modal
It's possible to see that the frame changed, and the values match with the 3D effect when a view is presenting another view, and it's never changing again to the right values unless you send the app again to the background or switch views (e.g. using a TabView).
I don't find anything on iOS release notes talking about it, so I supposed it must be a bug (I've filled out a bug report already).
On iOS 15, the frame value keeps stable at the same value.
I have a couple of views relying on the value of a GeometryReader, and it's causing my view to deform because of this issue. Does anyone know a way to force the recalculation for the GeometryReader for this case?
Any help is appreciated.
The issue won't occur if you control the display of the sheet with the new presentationDetents method, provided you do not request to cover the entire screen.
I modified your code as follows:
.sheet(isPresented: $isPresented) {
if #available(iOS 16, *) {
modalView
.presentationDetents([.fraction(0.99)])
}
else {
modalView
}
}
The issue will remain if you request .fraction(1), i.e. covering the whole screen.

XCode SwiftUI - Why is my keypad toolbar doing this?

Very novice to the app development game. I am trying to put this toolbar above the .decimalPad and I cannot get this large gap to go away.
VStack {
Rectangle()
.foregroundColor(Color(UIColor.systemBackground))
.frame(height: 35)
.overlay {
HStack {
Spacer()
Button(action: {
isTextFieldFocused = false
}) { Text("Done")}
}
.offset(y: -3)
.padding(.trailing)
}
.opacity(isTextFieldFocused ? 1 : 0)
.ignoresSafeArea(.keyboard) //This makes sure the bottom tab bar stays below the keyboard.
}
I initially thought it was something in another view causing the spacing, but I managed to parse through the views in the canvas and it does it regardless.
Here is what I'd like it to look like, for reference.
What I want
To add a Button onto your keyboard, you use a .toolbar with the locations to .keyboard like this:
TextField("Enter Text", text: $text)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button(action: {
isTextFieldFocused = false
}) { Text("Done")}
// If you want it leading, then use a Spacer() after
Spacer()
}
}
You were overthinking it by adding the Rectangle. This is why we look for minimal reproducible examples. We can dial in the fix for your specific code.

Slide Over in iPadOS 15 breaks NavigationLink (SwiftUI)

I’m having issues when using NavigationView and NavigationLinks on iPadOS 15. Currently running Dev Beta of iPadOS 15.3 (19D5026g), but I’ve had this issue since the release of 15.1. When I’m using my app as usual, nothing is wrong. But when I turn the app into a Slide Over, the detail works, but when I click “Back” and pop the detail back, I’m unable to click the NavLink (it doesn’t push the detail). When I turn the app back to full screen, everything is pretty much fine. Has anyone noticed something like that?
Edit: Just found out that Split Screen does the exact same thing.
Here’s my code:
struct ContentView: View {
var body: some View {
NavigationView {
SideBar()
.navigationBarTitle("SideBar")
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {}
//Button which opens options
}
DetailView()
.navigationBarTitle(“Detail”, displayMode: .large)
}
}
}
//SideBar:
struct SideBar: View {
var body: some View {
VStack{
Spacer()
VStack {
NavigationLink(destination: DetailView()) {
Text(“DetailView”)
.font(.headline)
}
NavigationLink(destination: OtherDetailView()) {
Text("Other Detail View")
.font(.headline)
}
}
Spacer()
}
}
}
//DetailView and OtherDetailView
struct DetailView: View {
var body: some View {
VStack {
Spacer()
Text("Hello World!")
Spacer()
}
.navigationBarTitle(“Detail”)
}
}```
Thanks for your help!
Found the solution myself some time ago, so I figured I'd post it here in case someone experiences the same behavior.
The right way to do what I was trying to do is apparently by using a list with this modifier:
.listStyle(SidebarListStyle())
That seemed weird to me, but I haven't found a different way to accomplish the thing I wanted to.

SwiftUI insertion transion is not working when view added

I'm trying SwiftUI tutorial in apple developer page. now I'm following transition tutorial but my transition is not working when the view added.
here my code.
VStack(alignment: .leading) {
HStack() {
// title
Text(titleText)
.font(.headline)
.padding()
Spacer()
// button
Button(action: {
withAnimation {
self.showDetail.toggle()
}
}) {
Image(systemName: "chevron.right.circle")
.imageScale(.large)
.rotationEffect(.degrees(showDetail ? 90 : 0))
.padding()
}
}
// detail
if showDetail {
Text(contentText)
.transition(.slide)
.padding()
}
}
I think the Text that has contentText, should appear with slide transition but when I press the Button, it just pop up. but when I press button again it disappear with transition. so removal transition is work.
How can I fix this?
After some test I found some solution.
solution 1:
It is just bug of XCode canvas.
It works in the simulator or on a real machine.
solution 2. (if you really want to do in canvas)
Button (action : {
withAnimation(.easeInOut) { // add animation manually
self.showDetail.toggle()
}
}) {
// label Image no change
}
if showDetail {
Text(contentText)
.transition(.slide)
.animation(.easeInOut) // match with withAnimation
}
Although I haven't done the tutorial you are talking about but I think it works pretty fine using the .move transition instead of the .slide transition.
Here's my code for the detail part (the rest is unchanged). I hope my answer can help you
if showDetail {
Text(contentText)
.transition(.move(edge: .leading))
.padding()
}