Unwanted white space on Sheet - swift

I have a basic tabview with 2 tabs. Tab 2 has a button to a modal sheet with a Page Style tabview imbedded in a Navigation Stack and added toolbar. When adding the Navigation Stack I get an unwanted white space at the bottom of the sheet view pages. I have tried using .ignoreSafeArea(edges: .bottom) in many places no solution and I'm stumped on this one. What am I missing here? How do I get rid of this unwanted white space? Is my navigation stack in the wrong place? Seems like such a simple design to be such a problem.
iOS 16.1
Xcode 14.2
struct HomeView: View {
#State private var pageIndex = 0
var body: some View {
TabView(selection: $pageIndex) {
PageOne()
.tabItem {
Label("Page 1", systemImage: "star")
}.tag(0)
PageTwo()
.tabItem {
Label("Page 2", systemImage: "bookmark")
}.tag(1)
}
}
}
struct PageOne: View {
var body: some View {
VStack {
Text("Page 1")
}
}
}
struct PageTwo: View {
#State private var sheetIsShowing = false
var body: some View {
VStack {
Text("Page 2")
Button("Show Sheet") {
self.sheetIsShowing.toggle()
}
}
.fullScreenCover(isPresented: $sheetIsShowing) {
SheetView(sheetIsShowing: $sheetIsShowing)
}
}
}
struct SheetView: View {
#Binding var sheetIsShowing: Bool
var body: some View {
NavigationStack {
TabView {
SheetTabView1()
SheetTabView2()
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
self.sheetIsShowing.toggle()
} label: {
Text("Cancel")
}
}
}
}
.ignoresSafeArea(edges: .bottom)
}
}
struct SheetTabView1: View {
#State private var text1 = ""
#State private var text2 = ""
var body: some View {
List {
TextField("Enter Text", text: $text1)
TextField("Enter Text", text: $text2)
}
}
}
struct SheetTabView2: View {
#State private var text1 = ""
#State private var text2 = ""
var body: some View {
List {
TextField("Enter Text", text: $text1)
TextField("Enter Text", text: $text2)
}
}
}

The problem here is the tabViewStyle view modifier. If you get rid of it, you'll get rid of the white space.

Related

Remove padding on TextEditor

My custom text editor below once you click on the pen to edit, a new space appears so the text from before is not on the same line as the new one. How can I fix this? Here's a simple reproducible example:
struct SwiftUIView: View {
#State var name: String = "test"
#State var showEdit: Bool = true
var body: some View {
HStack {
HStack {
if(showEdit) {
CustomTextEditor.init(placeholder: "My unique name", text: $name)
.font(.headline)
} else {
Text(name)
.font(.headline)
}
}
Spacer()
Button(action: {
showEdit.toggle()
}) {
Image(systemName: "pencil")
.foregroundColor(.secondary)
}
}
}
}
struct CustomTextEditor: View {
let placeholder: String
#Binding var text: String
var body: some View {
ZStack {
if text.isEmpty {
Text(placeholder)
.foregroundColor(Color.primary.opacity(0.25))
}
TextEditor(text: $text)
}.onAppear() {
UITextView.appearance().backgroundColor = .clear
}.onDisappear() {
UITextView.appearance().backgroundColor = nil
}
}
}
I want it to have the same padding properies as inserting a simple Text("") so when I switch between Text("xyz") and TextEditor(text: $xyz) it has the same padding alignment. Right now TextEditor has a weird padding.
You will drive yourself insane trying to line up a Text and a TextEditor (or a TextField, for that matter), so don't try. Use another, disabled, TextEditor instead, and control the .opacity() on the top one depending upon whether the bound variable is empty or not. Like this:
struct CustomTextEditor: View {
#Binding var text: String
#State private var placeholder: String
init(placeholder: String, text: Binding<String>) {
_text = text
_placeholder = State(initialValue: placeholder)
}
var body: some View {
ZStack {
TextEditor(text: $placeholder)
.disabled(true)
TextEditor(text: $text)
.opacity(text == "" ? 0.7 : 1)
}
}
}
This view will show the placeholder if there is no text, and hide the placeholder as soon as there is text.
Edit:
You don't need the button, etc. in your other view. It becomes simply:
struct SwiftUIView: View {
#State var name: String = ""
var body: some View {
CustomTextEditor.init(placeholder: "My unique name", text: $name)
.font(.headline)
.padding()
}
}
and if you need a "Done" button on the keyboard, change your CustomTextEditor() to this:
struct CustomTextEditor: View {
#Binding var text: String
#State private var placeholder: String
#FocusState var isFocused: Bool
init(placeholder: String, text: Binding<String>) {
_text = text
_placeholder = State(initialValue: placeholder)
}
var body: some View {
ZStack {
TextEditor(text: $placeholder)
.disabled(true)
TextEditor(text: $text)
.opacity(text == "" ? 0.7 : 1)
.focused($isFocused)
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button {
isFocused = false
} label: {
Text("Done")
.foregroundColor(.accentColor)
.padding(.trailing)
}
}
}
}
}

How to update UI of a view from another view [ SwiftUI Question]

I am new to Swift & iOS dev in general.
I am building an app in SwiftUI. Assume there are 2 separate views in different files: MainView() & Results() and TabBar()(ignore the naming, this is just an example)(those 2 views are ON 2 SEPARATE TABS)
My goal is to press on that button FROM ResultsView() and change the textValue property of MainView() to something else & then display the change ON the MainView(). How can I do that? P.S Please ignore alignment & etc. I am new to StackOverflow
struct MainView: View {
#State var textValue: String = "Old Text"
var body: some View {
Text(textValue)
}
}
struct TabBar: View {
var body: some View {
TabView {
MainView()
.tabItem {
Label("Main", systemImage: "circle.dashed.inset.filled")
}
ResultsView()
.tabItem {
Label("Results", systemImage: "person.2.crop.square.stack.fill")
}
}
}
}
struct ResultsView: View {
var body: some View {
Button {
// HOW TO UPDATE MainView.textValue?
} label: {
Text("Update")
}
}
}
ContentView() just has TabBar() in body
Basically, my question is how do we change UI of certain view from another view? Without NavigationLink
Might sound like a rookie question, but any reply will be so appreciated!
You can achieve this by passing the textValue from MainView to DetailView through a #Binding.
struct MainView: View {
#State private var text: String = "Original"
var body: some View {
NavigationView {
VStack {
Text(text)
NavigationLink("Detail", destination: DetailView(text: $text))
}
}
}
}
struct DetailView: View {
#Binding var text: String
var body: some View {
Button("Change Text") {
text = "New Text"
}
}
}
I recommend you read this article about bindings as it explains how to change a view from elsewhere.
Edit:
Because you mentioned, that both views are in a TabBar, here should be a working example:
struct ContentView: View {
#State private var text: String = "Original Text"
var body: some View {
TabView {
MainView(text: $text)
.tabItem { ... }
DetailView(text: $text)
.tabItem { ... }
}
}
}
struct MainView: View {
#Binding var text: String
var body: some View {
Text(text)
}
}
struct DetailView: View {
#Binding var text: String
var body: some View {
Button("Change Text") {
text = "New Text"
}
}
}

SwiftUI TabView Animation

I am currently facing a pb on my app.
I would like to animate the insertion and removal of items that are controlled by SwiftUI TabView.
Here is the simplest view I can come with that reproduce the problem
struct ContentView: View {
#State private var selection: Int = 1
var body: some View {
TabView(selection: $selection.animation(),
content: {
Text("Tab Content 1")
.transition(.slide) //could be anything this is for example
.tabItem { Text("tab1") }.tag(1)
.onAppear() {print("testApp")}
.onDisappear(){print("testDis")}
Text("Tab Content 2")
.transition(.slide)
.tabItem { Text("tab2") }.tag(2)
})
}
}
Actually when hitting a tabItem. It switches instantly from "Tab Content 1" to "Tab Content 2" and I would like to animate it (not the tab item button the actuel tab content). The On Appear and onDisapear are corectly called as expected hence all transition should be triggered.
If someone has an idea to start working with I would be very happy
Thanks
1.with .transition() we only specify which transition should happen.
2.Transition occur (as expected), only when explicit animation occurs.
3.Animation occurs when change happened(State, Binding)
here is one of possible approaches.
struct ContentView: View {
#State private var selection: Int = 1
var body: some View {
TabView(selection: $selection,
content: {
ItemView(text:"1")
.tabItem { Text("tab1") }.tag(1)
ItemView(text: "2")
.tabItem { Text("tab2") }.tag(2)
})
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct ItemView: View {
let text: String
#State var hidden = true
var body: some View {
VStack {
if !hidden {
Text("Tab Content " + text)
.transition(.slide)
}
}
.onAppear() { withAnimation {
hidden = false
}}
.onDisappear(){hidden = true}
}
}

SwiftUI: How can I customize the sheet only for iPad?

I´m new in SwiftUI and I have the following problem. When I click the button on the iPhone the following comes up
but when I press the button on the iPad the following comes up
The picker is not on the whole sheet. How can I show the picker on the iPad on the whole sheet like on the iPhone?
struct ContentView: View {
#State var value_2 = 1
#State var show_1 = false
var body: some View {
VStack {
Button(action: {
self.show_1.toggle()
}) {
Text("Push")
}
.sheet(isPresented: $show_1) {
Sheet(show_0: self.$show_1, value_1: self.$value_2)
}
}
}
}
struct Sheet: View {
#Binding var show_0: Bool
#Binding var value_1: Int
var body: some View {
NavigationView {
number(value_0: $value_1)
.navigationBarTitle(Text("Enter number"), displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
self.show_0 = false
}) {
Text("Done").bold()
})
}
}
}
struct number: View {
#Binding var value_0: Int
var body: some View {
Section {
Text("Headline")
Picker("",selection: $value_0)
{
ForEach(1..<101) { value in
Text("\(value)")
}
}
}
.labelsHidden()
}
}
As far as I understood your intention, you need just change a style of navigation view, like
var body: some View {
NavigationView {
number(value_0: $value_1)
.navigationBarTitle(Text("Enter number"), displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
self.show_0 = false
}) {
Text("Done").bold()
})
}
.navigationViewStyle(StackNavigationViewStyle()) // << here !!
}

SwiftUI hiding a navigation bar only when looking at ContentView

I have a Content file and am hiding the navigation bar because it takes up space and pushes elements down. One of the buttons in the ContentView redirects (using a navigation link) to another view. In this other view, the navigationBar is still hidden....for simplicity sake, I'll cut out some of the code from ContentView:
//this is the view that looks "fine" (i.e. the navigation bar takes up no space)
struct ContentView: View {
#State private var isPresentedSettings = false
var body: some View {
NavigationView {
ZStack {
VStack {
SettingsButton(isPresentedSettings: $isPresentedSettings)
}
}.navigationBarTitle("").navigationBarHidden(true)
}
}
}
//this is the button that pulls up the settings page view
struct SettingsButton: View {
#Binding var isPresentedSettings: Bool
var body: some View {
NavigationLink (destination: SettingsPageView(isPresentedSettings:
self.$isPresentedSettings)) {
Button(action: { self.isPresentedSettings.toggle() }, label: { Text("Button") })
}
}
}
//This is the view that should have a navigationbar but it doesn't
struct SettingsPageView: View {
#Binding var isPresentedSettings: Bool
var body: some View {
NavigationView {
VStack {
Text("This is a view")
}.navigationBarTitle("Settings", displayMode: .inline)
}
}
}
Also...there may have been typos because I just copied the code over from another computer. Sorry and thank you in advance!
Firstly, you don't need to have this isPresentedSettings variable for presenting a NavigationLink.
NavigationLink(destination: SettingsPageView()) {
Text("Button")
}
And there should be only one NavigationView in your view hierarchy.
This is how your final code can look like:
struct ContentView: View {
#State private var navBarHidden = true
var body: some View {
NavigationView {
ZStack {
VStack {
SettingsButton(navBarHidden: $navBarHidden)
}
}
.navigationBarHidden(navBarHidden)
}
}
}
struct SettingsButton: View {
#Binding var navBarHidden: Bool
var body: some View {
NavigationLink(destination: SettingsPageView(navBarHidden: $navBarHidden)) {
Text("Show View")
}
}
}
struct SettingsPageView: View {
#Binding var navBarHidden: Bool
var body: some View {
VStack {
Text("This is a view")
}
.navigationBarTitle("Settings", displayMode: .inline)
.onAppear {
self.navBarHidden = false
}
.onDisappear {
self.navBarHidden = true
}
}
}