Unable click on segmented picker in swift ui - ios

I'm reusing one view for different scenarios. One is as modal window and second one is also modal but within navigation view.
My problem is that I can switching between options in picker only if view is as modal and not navigation view. If is within navigation view i'm not able to click on any option presented in picker.
ZStack {
Color("my-orange").edgesIgnoringSafeArea(.top)
VStack(alignment: .leading) {
if !self.picture {
Image("van")
.resizable()
.frame(width: UIScreen.main.bounds.width, height: 275)
.padding(.leading)
}
Picker(selection: $langauge, label: Text("Test")) {
ForEach(0..<contentLanguages.languages.count, id:\.self) { index in
Group {
if self.contentLanguages.languages[index].isOn {
Text(self.contentLanguages.languages[index].country).tag(index)
.foregroundColor(.white)
}
}
}
}.pickerStyle(SegmentedPickerStyle())
.padding([.leading, .trailing], 60)
.padding([.top, .bottom])
.background(Color("my-orange"))
Spacer()
TabBar(index: $index)
.offset(y: self.picture ? -30 : 0)
}.edgesIgnoringSafeArea(.all)
}
This is how the view looks where I'm not able to switch between options
And here I can switch within same view but not opened as navigation view
Update:
What I found out there is problem with .edgeIgorningSafeArea. When I removed this part of the code i'm able to switch on both scenarios but i want to keep switcher always on the top.

I have figure out by moving picker to the view where is NavigationView -> NavigationLink.navigationBarItems
And origin picker is under if statement and shows when is needed.
My question now is: if i have picker as navigationBarItems how I can handle events on that view?

Related

SwiftUI - How to prevent keyboard in a sheet to push up my main UI

I'm using sheets (SwiftUI) during an onboarding to let people enter text, however whenever the sheet is dismissed, the elements in the background move, as if the keyboard was pushing them up and down. If the keyboard is not on screen, the elements in the background don't move when the sheet is dismissed. I've tried to use .ignoresSafeArea(.keyboard, edges: .bottom) but it doesn't seem to work.
Any idea regarding how to "fix" the background elements while a sheet with a keyboard is dismissed?
private var welcomeSection5: some View {
ZStack {
VStack {
// This is the part that moves up and down
TellSlideView(text: "And what's your age \(userName) if I may?")
Spacer()
Button(action: {
displaySheet.toggle()
}, label: {
Text("Enter age")
.padding()
.foregroundColor(Color.white)
.frame(maxWidth: .infinity)
.background(Color.MyTheme.Purple)
.cornerRadius(15)
.padding(.horizontal)
.padding(.bottom, 40)
})
}.sheet(isPresented: $displaySheet) {
AddUserAgeView(onboardingState: $onboardingState)
}.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
I had a similar problem once. I can't reproduce your code, but you might try using GeometryRadar like this:
GeometryReader { geometry in
ZStack {
VStack {
// This is the part that moves up and down
}
}
}
.ignoresSafeArea(.keyboard, edges: .bottom)

SwiftUI - How to programatically scroll to top and show a large navigation title?

Recently added to iOS 14 ScrollViewReader added a way to programmatically scroll to a view in SwiftUI.
Q: Is there a way to scroll past the topmost element in ScrollView and reveal a large navigation title?
Expected effect:
Large navigation title after scrolling to top
Outcome:
Inline navigation title after scrolling to top
Code snippet for reference:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ScrollViewReader { proxy in
ScrollView {
Rectangle()
.fill(.yellow)
.aspectRatio(contentMode: .fit)
.id("scrollID")
Rectangle()
.fill(.blue)
.aspectRatio(contentMode: .fit)
Rectangle()
.fill(.green)
.aspectRatio(contentMode: .fit)
Rectangle()
.fill(.red)
.aspectRatio(contentMode: .fit)
Button("Scroll to top") {
withAnimation {
proxy.scrollTo("scrollID")
}
}
}
.navigationTitle("Navigation")
}
}
}
}
This code scrolls to a top rectangle on the button tap but it doesn't reveal the default large navigation title. How could I achieve that?
(I haven't tested it!) Try:
declare state var to toggle navigation display mode
add .navigationBarTitle("Home", displayMode: showLargeDisplayMode ? .large : .inline)
toggle showLargeDisplayMode after proxy.scrollTo("scrollID")

Issues with a custom tab view combined with ScrollView

The look of my tab view is great. Everything in fact looks good. But with the current code. The ScrollView ends behind the tab bar instead of above. I'm guessing it has to do with it being a ZStack (it - meaning the content).
I tried adding padding on each view inside the switch (padding bottom same height as tab bar but not working).
The two results I've gotten is either it won't scroll enough to see all content but tab bar looks good.
OR
Scrolling works great but the tab bar has some overlaying issues visually with showing whitespace.
I just don't know how to move around the code any more to get a better result. :(
//Landing-page..
VStack {
ZStack(alignment: Alignment(horizontal: .center, vertical: .bottom)) {
switch tabController.selectedIndex {
case 0:
HomeView()
.padding(.bottom, 50)
case 1:
Text("Test..")
case 2:
Text("Test...")
case 3:
Text("Test....")
default:
VStack {
Text("Default View")
}
}
CustomTabView()
}
}
.background(Color.gray.ignoresSafeArea()).edgesIgnoringSafeArea(.bottom)
// TabView..
HStack {
// tabControllers.icons.count = 4 tabs at the moment
ForEach(0..<tabController.icons.count, id: \.self) { tab in
Button(action: {
// animation when changing tab comes here eventually
tabController.selectedIndex = tab
}, label: {
Spacer()
Image(systemName: tabController.icons[tab])
.frame(width: 50, height: 50)
.font(.system(
size: 22,
weight: tabController.selectedIndex == tab ? .semibold : .regular,
design: .default))
.foregroundColor(tabController.selectedIndex == tab ? Color.themeAccent : Color.themeAccent.opacity(0.3))
.background(tabController.selectedIndex == tab ? Color.primaryPurple : nil)
.cornerRadius(tabController.selectedIndex == tab ? 30 : 0)
Spacer()
})
}
}
.padding()
.background(Color.blue)
.clipShape(CShape())
.shadow(color: Color.black.opacity(0.3), radius: 20, x: 0.0, y: 0.0)
The ScrollView starts at the top in the HomeView.
Tab view showing correctly, but not scrollview
ScrollView showing correctly, but not tab view
I think you can get rid of the ZStack, put the switch/case in the VStack and the CustomTabView() below that. The ScrollView will push the CustomTabView to the bottom and not overlap it. If not all of the views in the switch are ScrollViews, then you can add a Spacer() above the CustomTabView, which will push the views apart to the top and bottom.

Textfield tap is dismissing navigation link in SwiftUI

I'm facing a weird issue with textfields and navigation view/link using SwiftUI
All I do is navigate through views using navigation links and inside the destination view there are some textfields. When I tap on any of them the navigation automatically dismisses.
How can I fix navigation link from dismissing when textfield is tapped on and the keyboard shows up?
var emailLoginButton: some View {
NavigationLink(destination: LoginView(viewModel: .init(mode: .login, isPushed: $viewModel.authViewPushed)), isActive: $viewModel.authViewPushed) {
Button(action: { viewModel.authViewPushed = true }) {
HStack {
Image(systemName: "envelope")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.foregroundColor(.white)
Text("continue_with_email".localized())
.padding(.horizontal, 20)
}
}
.padding()
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.background(Capsule().fill(Color.primaryPurple))
.shadow(color: Color.black.opacity(0.15), radius: 5, x: 5, y: 5)
.padding(.horizontal)
.padding(.bottom, 20)
}
.isDetailLink(false)
}
// Destination's view textfield which "dismisses" navigationLink
var emailTextField: some View {
HStack {
Image(systemName: "envelope")
.font(.title2)
.foregroundColor(.primary)
.frame(width: 35)
TextField(viewModel.emailPlaceholderText.uppercased(), text: $viewModel.email)
.autocapitalization(.none)
}
.padding()
.background(Color.white.opacity(viewModel.email == stringEmpty ? 0 : 0.12))
.cornerRadius(12)
.padding(.horizontal)
}
There seems to be an issue with iOS 14.5 and up where the tapping of any TextField inside a NavigationLink destination makes the view to pop out to the previous one.
There is a workaround thanks to #SeitenWerk and documented on the Apple Developer Forums
https://developer.apple.com/forums/thread/677333
The solution is simple. Just add an empty NavigationLink next to the one that fails. Interestingly, it logs "Unable to present. Please file a bug." on the debug console but makes things right with the usability of the app
NavigationLink(destination: LoginView()){
Text("LOGIN")
}
NavigationLink(destination: EmptyView()) {
EmptyView()
}
Remember to thank SeitenWerk on the Developer Forums.
After some time of research I've found that NavigationLink closes because authViewPushed parameter in the viewModel becomes false. That happens because viewModel is being recreated because of firstView update. I've faced the same issue and here is the solution:
struct MyView: View {
#StateObject var viewModel = MyViewModel()
var body : some View {
}
}
In this case MyView is being updated but MyViewModel remains the same.
on iOS 15
NavigationView {
//your staff
}
.navigationViewStyle(StackNavigationViewStyle())
This might be one of the most frustrating and egregious examples of "this works everywhere else on every other version of whatever" I've seen of late in SwiftUI. (sigh) I'm reworking the whole workflow on two Views just to support previous versions of iOS which I didn't initially set out to do. Grrrr... Are there any known workarounds that are consistently proven to work?
For me the solution is to force out focus of the text field by focus state:
#FocusState private var nameIsFocused: Bool
TextField("text", text: $text)
.textInputAutocapitalization(.never)
.textContentType(.username).keyboardType(.asciiCapable)
.focused($nameIsFocused)
And then in the button callback
var timer:Timer?
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false) { _ in
moveToScreen.toggle(); // here change the state}

Add button to navigationBarTitle Swift ui

Im working in swift ui. I want to put a button on the side of the NavigationBar title.
I want to be able to click the user image and navigate to another view
Can this be done?
The buttons are placed in navigation bar using .navigationBarItems(). Any view can be used inside a Button, so a button almost like the one in your image can be declared like this:
var body: some View {
NavigationView {
// the rest of your UI components
.navigationBarTitle("Browse")
.navigationBarItems(trailing: Button(action: {}) {
VStack {
Spacer()
Image("name")
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
}
})
}
}
Please note that it has a slightly different alignment (is going to get drawn a bit higher than your example).
This should solve the problem with the offset, but it is very hacky. And maybe there is a better answer to your problem than this. But if you are ok with this answer please give LuLuGaGa an upvote as I have copied a lot from him. And I did not come up with that answer myself, but I can not remember where I found the original answer.
NavigationView {
// the rest of your UI components
.navigationBarTitle("") // To hide the real navigationBarTitle
.navigationBarItems(leading:
Text("Browse").font(.largeTitle).bold().padding(.top, 10), // To add a fake navigationBarTitle
trailing: Button(action: {}) {
VStack {
Spacer()
Image("swiftui")
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
}
} .buttonStyle(PlainButtonStyle()) // You should also add that to your code otherwise the picture will turn blue
)
}
var body: some View {
NavigationView {
Text("SwiftUI")
.navigationBarTitle("Welcome")
.navigationBarItems(trailing:
Button("Bar button") {
print("Bar button!")
}
)
}
Try this
NavigationView{
.navigationBarTitle("Browse")
.navigationBarItems(trailing:
Button(action: { // Move to next View }){
Image()
}
)
}

Resources