Aligning a Text and TextField inside the Section of a Form - swiftui

Another simple SwiftUI tasks that is causing me more trouble than it should.
I can't figure a way to align the Text and TextField correctly.
None of the HSTack alignment seem to yield acceptable results.
import SwiftUI
struct SignIn: View {
#State var email: String = ""
var body: some View {
VStack {
Text("Sign In")
.font(.largeTitle)
Form {
Section {
HStack {
Text("ID")
TextField("Email", text: $email)
}
}
}
}
}
}
struct SignIn_Previews: PreviewProvider {
static var previews: some View {
SignIn()
}
}

You mentioned trying different HStack alignments, did you try .firstTextBaseline or .lastTextBaseline? Both of these align the Text and TextField correctly for me. So that line becomes
HStack(alignment: .lastTextBaseline) {
Screenshot of Result
Full code:
import SwiftUI
struct SignIn: View {
#State var email: String = ""
var body: some View {
VStack {
Text("Sign In")
.font(.largeTitle)
Form {
Section {
HStack(alignment: .lastTextBaseline) {
Text("ID")
TextField("Email", text: $email)
}
}
}
}
}
}
struct SignIn_Previews: PreviewProvider {
static var previews: some View {
SignIn()
}
}

One solution is to use another TextField instead of a Text:
HStack {
TextField("", text: .constant("ID"))
.fixedSize()
.disabled(true)
TextField("Email", text: $email)
Spacer()
}
It's kinda ugly though.

I was having this same issue, and the answer is simpler than I thought. I ended up adding the modifier .aspectRatio(.fit) and that fixed it for me.
Hope this helps!

Related

How do I use a NavigationLink in SwiftUI?

I am new to Xcode and work on a Login Page.
I try to link all pages with each other but even if I saw these Code in a tutorial working it doesn't work for me.
I hope you can help me, I would appreciate it a lot?
In NavigationLink the order of parameters is very important
here is an example that may help you
struct Page1: View {
#Binding var GoToPage2: Bool
var body: some View {
VStack{
Button("Go To Page"){
GoToPage2 = true
print(GoToPage2)
}
NavigationLink(destination: Page2(), isActive: $GoToPage2){
EmptyView()
}
}
.navigationTitle("Page 1")
}
}
struct Page2: View {
var body: some View {
Text("page 2!")
.padding()
.navigationTitle("Page 2")
}
}
struct ContentView: View {
#State var GoToPage2 = false
var body: some View {
NavigationView{
VStack{
NavigationLink(destination: Page1(GoToPage2: $GoToPage2)){
Text("Signe In")
.padding()
.background(.regularMaterial)
.colorScheme(.dark)
.cornerRadius(12)
.font(.largeTitle.bold())
.foregroundColor(.primary)
}
}
.navigationTitle("App")
}
}
}
You can use NavigationLink in many different ways. Documentation here
struct ContentView: View {
#State var active: Bool = false
var body: some View {
NavigationView {
List {
NavigationLink(destination: EmptyView()) {
Label("Badge", systemImage: "app.badge")
}
NavigationLink("General", destination: EmptyView())
NavigationLink("About", destination: EmptyView(), isActive: $active)
}
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("View1")
// put the second view in destination in which you want to reach
// click in view2, this code will redirect from view1 to view2
NavigationLink(destination: View2()){
Text("View2")
}
}
}
}
}

SwiftUI passing selected date from modal to parent variables

Need help with this please.
I have a view with 2 date variables and I want to show a modal which have the datepicker and let user pick different dates for these variables.
Currently I have two buttons that show the same sheet but pass different variable to the modal.
The problem the variable don’t update after dismissing the modal.
import SwiftUI
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
import SwiftUI
struct ContentView: View {
#State private var secOneDate = Date()
#State private var secTwoDate = Date()
#State private var isDatepickerPresented = false
var body: some View {
VStack {
HStack{
Button{
isDatepickerPresented = true
} label: {
Image(systemName: "calendar")
.imageScale(.large)
.foregroundColor(.indigo)
}
.sheet(isPresented: $isDatepickerPresented){
DatePickView(selectDate: $secOneDate)
}
Text("SecOneDate: \(secOneDate.formatted(date: .abbreviated, time: .shortened))")
}
.padding()
HStack{
Button{
isDatepickerPresented = true
} label: {
Image(systemName: "calendar")
.imageScale(.large)
.foregroundColor(.mint)
}
.sheet(isPresented: $isDatepickerPresented)
{
DatePickView(selectDate: $secTwoDate)
}
Text("SecTwoDate: \(secTwoDate.formatted(date: .abbreviated, time: .shortened))")
}
.padding()
}
}
}
import SwiftUI
struct DatePickView: View {
#Environment(\.dismiss) private var dismiss
#Binding var selectDate: Date
var body: some View {
VStack(alignment: .center, spacing: 20) {
HStack {
Text("\(selectDate)")
.padding()
Spacer()
Button {
dismiss()
} label: {
Image(systemName: "delete.backward.fill")
.foregroundColor(.indigo)
}
}.padding()
DatePicker("", selection: $selectDate)
.datePickerStyle(.graphical)
}
}
}
First of all, thank you for your minimal, reproducible example: it is clear and can be immediately used for debugging. Answering to your question:
The problem with your code is that you have only one variable that opens the sheet for both dates. Even though you are correctly passing the two different #Bindings, when you toggle isDatepickerPresented you are asking SwiftUI to show both sheets, but this will never happen. Without knowing, you are always triggering the first of the sheet presentations - the one that binds secOneDate. The sheet that binds secTwoDate is never shown because you can't have two sheets simultaneously.
With that understanding, the solution is simple: use two different trigger variables. Here's the code corrected (DatePickView doesn't change):
struct Example: View {
#State private var secOneDate = Date()
#State private var secTwoDate = Date()
#State private var isDatepickerOnePresented = false
#State private var isDatepickerTwoPresented = false
var body: some View {
VStack {
HStack{
Button{
isDatepickerOnePresented = true
} label: {
Image(systemName: "calendar")
.imageScale(.large)
.foregroundColor(.indigo)
}
.sheet(isPresented: $isDatepickerOnePresented){
DatePickView(selectDate: $secOneDate)
}
Text("SecOneDate: \(secOneDate.formatted(date: .abbreviated, time: .shortened))")
}
.padding()
HStack{
Button{
isDatepickerTwoPresented = true
} label: {
Image(systemName: "calendar")
.imageScale(.large)
.foregroundColor(.mint)
}
.sheet(isPresented: $isDatepickerTwoPresented) {
DatePickView(selectDate: $secTwoDate)
}
Text("SecTwoDate: \(secTwoDate.formatted(date: .abbreviated, time: .shortened))")
}
.padding()
}
}
}

SwiftUI image in modal sheets for Apple Watch

I would like to create a modal sheets with an image in the body, like in Human Interface Guidelines. Could you please give me an example?
https://developer.apple.com/design/human-interface-guidelines/watchos/interaction/modality/
The following code basically recreates the example screenshot from above:
import SwiftUI
struct ModalView: View {
#Binding var isPresented: Bool
var body: some View {
VStack {
Image(systemName: "headphones")
.font(.system(size: 30))
Text("To play audio, connect Bluetooth headphones to your Apple Watch.")
.font(.footnote)
.multilineTextAlignment(.center)
}
.opacity(0.8)
.padding(10)
Spacer()
Button("Connect a Device") {
isPresented.toggle()
}.padding(.horizontal)
}
}
struct TestView: View {
#State private var isPresentingModalView = false
var body: some View {
Button("Connect") {
isPresentingModalView.toggle()
}
.fullScreenCover(isPresented: $isPresentingModalView) {
ModalView(isPresented: $isPresentingModalView)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
Result:
See docs:
https://developer.apple.com/documentation/swiftui/view/fullscreencover(ispresented:ondismiss:content:)

Passing data between views in SwiftUI?

I'm trying to pass a few simple data (strings and URL and images) from one view to another view in my swiftui app.
This simple task proves to be a headache at the moment.
This is what I have done so far:
in my firstView I have this:
struct Book {
var title: String
var author: String
}
struct firstView: View {
#State private var showAudioPlayer = false
#Binding var tabSelection: Int
init(tabSelection: Binding<Int>) {
_tabSelection = tabSelection
}
var body: some View {
NavigationView {
ZStack {
Text("Tap Here")
.onTapGesture {
self.showAudioPlayer.toggle()
}
}
}
.fullScreenCover(isPresented: $showAudioPlayer, content: secondView.init)
}
}
And this is what I have in my secondView:
struct secondView: View {
#Environment(\.presentationMode) var presentationMode
var book: Book
var body: some View {
NavigationView {
ZStack{
VStack {
}
}
.background(Color.clear)
.navigationBarTitle("", displayMode: .inline)
.navigationBarItems(
leading: Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "chevron.down.circle")
.foregroundColor(Color.black)
})
.frame(maxWidth: .infinity, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
}
.edgesIgnoringSafeArea(.all)
}
}
However, when I try to compile my code, I get this error:
The error itself doesn't make much sense and I'm stuck.
what am I doing wrong?
Memberwise Initializers for Structure Types
The compiler takes a peak into the struct and sees two non-optional properties and thinks they should be in the constructor. It doesn't know the #Environment is going to magically be set by the framework, so it includes that property. As #Baglan said above, you are on the hook for the Book and need to pass it in.
The error message is saying "you asked me to instance audioPlayerView and I need a couple of parameters to make that happen", but you really only need one.

SwiftUI Navigation Controller stuttering with two Navigationlinks per List row

I am trying to create two NavigationLinks in a repeating List. Each has a separate destination. The code all works fine until I imbed the call to the root view in a List/ForEach loop. At which point the navigation becomes very strange.
Try to click on either link and then click the back indicator at the top. It will go to one NavigationLink, and then the other. Sometimes in a different order, and sometimes it will auto-return from one of the links, and othertimes it won't open the second detail view until you return from the first detail view. It does this both in Preview, as well as if you build and run the application.
I have distilled down the code to the most basic below. If you comment the 2 lines as indicated in ContentView, you will then see correct behavior.
I am running Catalina 10.15.5, xCode 11.6, with the application target of IOS 13.6.
How can I modify the code, so that it will work with the List/ForEach loop?
import SwiftUI
struct DetailView1: View {
var body: some View {
HStack {
Text("Here is Detail View 1." )}
.foregroundColor(.green)
}
}
struct DetailView2: View {
var body: some View {
HStack {
Text( "Here is Detail View 2.") }
.foregroundColor(.red)
}
}
struct RootView: View {
var body: some View {
HStack {
VStack {
NavigationLink(destination: DetailView1())
{ VStack { Image(systemName: "ant.circle").resizable()
.frame(width:75, height:75)
.scaledToFit()
}.buttonStyle(PlainButtonStyle())
Text("Tap for Detail 1.")
.foregroundColor(.green)
}
}
NavigationLink(destination: DetailView2())
{ Text("Tap for Detail 2.")
.foregroundColor(.red)
}
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
// Comment the following line for correct behavior
List { ForEach(0..<3) {_ in
RootView()
// Comment the following line for correct behavior
} }
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ContentView()
.navigationBarTitle("Strange Behavior")
}
}
}
In your case both navigation links are activated at once user tap a row, to avoid this below is possible approach
Tested with Xcode 12 / iOS 14
The idea is to have one link which is activated programmatically and destination is selected dynamically depending on which button is clicked
struct RootView: View {
#State private var isFirst = false
#State private var isActive = false
var body: some View {
HStack {
VStack {
Button(action: {
self.isFirst = true
self.isActive = true
})
{ VStack { Image(systemName: "ant.circle").resizable()
.frame(width:75, height:75)
.scaledToFit()
}
Text("Tap for Detail 1.")
.foregroundColor(.green)
}
}
Button(action: {
self.isFirst = false
self.isActive = true
})
{ Text("Tap for Detail 2.")
.foregroundColor(.red)
}
.background(
NavigationLink(destination: self.destination(), isActive: $isActive) { EmptyView() }
)
}
.buttonStyle(PlainButtonStyle())
}
#ViewBuilder
private func destination() -> some View {
if isFirst {
DetailView1()
} else {
DetailView2()
}
}
}