Background doesn't show up in the View - ios

I have ready view with background but when i call it here, i don't see it. What should i do?
When i deleted Form{}, my background appeared.
import SwiftUI
struct HomeView: View {
#State private var salaryPh: String = "" // should be int
var body: some View {
NavigationView {
ZStack {
BackgroundView()
Form {
Section(header: Text("Your netto-salary per hour")) {
TextField("My salary is...", text: $salaryPh)
}
}
}
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}

The opaque Form is on top of the BackgroundView, so hides it.
To make your BackgroundView visible, just add
.scrollContentBackground(.hidden)
to your From, e.g.
struct ContentView: View {
#State private var salaryPh: String = "" // should be int
var body: some View {
NavigationView {
ZStack {
BackgroundView()
Form {
Section(header: Text("Your netto-salary per hour")) {
TextField("My salary is...", text: $salaryPh)
}
}
.scrollContentBackground(.hidden)
}
}
}
}
struct BackgroundView: View {
var body: some View {
Color.red
}
}

Related

Scroll to correct position in SwiftUI when a list item is clicked

I've a SwiftUI ForEach loop which contains views that expand when clicked. Something similar to the view shown below.
struct ExpandingView: View {
#State var isExpanded = false
var body: some View {
VStack {
Text("Hello!")
if isExpanded {
Text("World")
}
}
.onTapGesture {
withAnimation {
isExpanded.toggle()
}
}
}
}
In theory, I can use the ScrollViewReader and scrollTo a particular position.
ScrollViewReader { view in
ScrollView {
ForEach(0...100, id: \.self) { id in
ExpandingView()
.id(id)
.padding()
}
}
}
However, in practice, I'm not sure how to get the id from within the view (because onTapGesture is in the view) and propagate it up one level.
The other option is to have the onTapGesture in the ForEach loop, but then I'm not sure how to toggle the isExpanded flag for the correct view.
You can pass a ScrollViewProxy to row view and then you can now able to scroll.
struct ExpandingView: View {
#State var isExpanded = false
var id: Int
var proxy: ScrollViewProxy
var body: some View {
VStack {
Text("Hello!")
if isExpanded {
Text("World")
}
}
.onTapGesture {
withAnimation {
isExpanded.toggle()
proxy.scrollTo(id, anchor: .center)
}
}
}
}
struct TestScrollView: View {
var body: some View {
ScrollViewReader { view in
ScrollView {
ForEach(0...100, id: \.self) { id in
ExpandingView(id: id, proxy: view)
.id(id)
.padding()
}
}
}
}
}
if I understand your question correctly, you are asking how to pass the id in "ContentView ScrollView"
into ExpandingView. Try this:
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ExpandingView: View {
#State var worldId: Int
#State var isExpanded = false
var body: some View {
VStack {
Text("Hello!")
if isExpanded { Text("World \(worldId)") }
}.onTapGesture {
withAnimation { isExpanded.toggle() }
}
}
}
struct ContentView: View {
var body: some View {
ScrollViewReader { view in
ScrollView {
ForEach(0...100, id: \.self) { id in
ExpandingView(worldId: id).id(id).padding()
}
}
}
}
}

How to add view which will be top of all view in swiftUI

I am working on miniplayer in swiftUI so a rectangle view will be universally top across the application. I have tried about ZStack but currently its only achieving on single view. Can you please tell how can I create above all view ?
You can put in in the App.body over ContentView and use some environment or environment object to control its visibility from any place in view hierarchy, like
#main
struct SwiftUIApp: App {
#State private var showView = false
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.showCoverView, $showView)
.overlay(Group {
if showView {
Text("Your top view here") // << here !!
}
})
}
}
}
struct ShowCoverViewKey: EnvironmentKey {
static var defaultValue: Binding<Bool> = .constant(false)
}
extension EnvironmentValues {
var showCoverView: Binding<Bool> {
get { self[ShowCoverViewKey.self] }
set { self[ShowCoverViewKey.self] = newValue }
}
}
You can do that with #EnvironmentObject
ContentView.swift
import SwiftUI
class ContentViewModel: ObservableObject {
#Published var isPresented: Bool = false
}
struct ContentView: View {
#EnvironmentObject private var miniPlayer: ContentViewModel
var body: some View {
ZStack {
VStack {
if miniPlayer.isPresented == true {
HStack {
Color.blue
// your miniPlayer View
}
.frame(width: 200, height: /*#START_MENU_TOKEN#*/100/*#END_MENU_TOKEN#*/, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
}
Spacer()
Button("Show/hide MiniPlayer") {
miniPlayer.isPresented.toggle()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(ContentViewModel())
}
}
YourApp.swift
import SwiftUI
#main
struct ReplyToStackoverflowApp: App {
var contentVM: ContentViewModel = ContentViewModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(contentVM)
}
}
}

SwiftUI - reusable components with links to other views as parameters

I would like to create reusable components in my app.
I have searched for similar problem. But I have only found much more complex examples.
Let's try this simple example - a button that could open different Views based on passed parameter.
I have 2 views that I will open as a sheet:
FirstView.swift
import SwiftUI
struct FirstView: View {
var body: some View {
Text("First view")
}
}
SecondView.swift
struct SecondView: View {
var body: some View {
Text("Second view")
}
}
ButtonView.swift
This is a view I would like to use as a reusable component in my design system.
import SwiftUI
struct ButtonView: View {
#State private var showModal: Bool = false
// This works
var text: String
// Here I am getting an error:
// Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements
var link: View
var body: some View {
VStack {
Spacer()
Button(action: {
self.showModal = true
}) {
Text(text)
.padding(20)
.foregroundColor(Color.white)
}.sheet(isPresented: self.$showModal) {
link
}
.background(Color.blue)
}
}
}
struct ButtonView_Previews: PreviewProvider {
static var previews: some View {
ButtonView(text: "TEST", link: FirstView())
}
}
ContentView.swift Here I am trying to use the same button component, but with different labels and links.
import SwiftUI
struct ContentView: View {
var body: some View {
HStack {
ButtonView(text: "first", link: FirstView())
.padding()
ButtonView(text: "second", link: SecondView())
.padding()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Passing String parameters works. Labels are different. But I cannot make it work with links to different Views. I am getting an error:
Protocol 'View' can only be used as a generic constraint because it
has Self or associated type requirements
Keeping First view and Second View as the same, use the following for the ButtonView:
struct ButtonView<Content : View>: View {
#State private var showModal: Bool = false
var text: String
// This is the generic content parameter
let content: Content
init(text: String, #ViewBuilder contentBuilder: () -> Content){
self.text = text
self.content = contentBuilder()
}
var body: some View {
VStack {
Spacer()
Button(action: {
self.showModal = true
}) {
Text(text)
.padding(20)
.foregroundColor(Color.white)
}.sheet(isPresented: self.$showModal) {
content
}
.background(Color.blue)
}
}
}
Here the generic parameter named content is used to receive any view and the initializer is used with the #ViewBuilder property wrapper to build the view.Now use it in the following way in ContentView struct:
struct ContentView: View {
var body: some View {
HStack {
ButtonView(text: "First") {
FirstView()
}
ButtonView(text: "Second") {
SecondView()
}
}
}
}
It will work like a charm :)
Also if you want to keep preview for ButtonView and don't want it to crash then add the preview as:
struct ButtonView_Previews: PreviewProvider {
static var previews: some View {
ButtonView(text: "First") {
FirstView()
}
}
}

Prevent SwiftUI from resetting #State

I don't often understand when SwiftUI resets the state of a view (i.e. all that is marked with #State). For example, take a look at this minimum example:
import SwiftUI
struct ContentView: View {
#State private var isView1Active = true
let view1 = View1()
let view2 = View2()
var body: some View {
VStack {
if isView1Active {
view1
} else {
view2
}
Button(action: {
self.isView1Active.toggle()
}, label: {
Text("TAP")
})
}
}
}
struct View1: View {
#State private var text = ""
var body: some View {
TextField("View1: type something...", text: $text)
}
}
struct View2: View {
#State private var text = ""
var body: some View {
TextField("View2: type something...", text: $text)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I'd want the two TextField to keep their content, but if you run this example some weird behaviours occur:
If you run the example on the preview only the View1 TextField content persists:
If you, instead, run the example on the simulator (or on an actual device) neither the first textfield content, nor the second one persist:
So, what's happening here? Is there a way to tell SwiftUI not to reset #State for a view? Thanks.
The issue is that View1 and View2 are being recreated every time isView1Active is changed (because it is using #State which reloads the body of ContentView).
A solution would be to keep the text properties of the TextFields in the ContentView as shown below and use #Binding:
struct ContentView: View {
#State private var isView1Active = true
#State private var view1Text = ""
#State private var view2Text = ""
var body: some View {
VStack {
if isView1Active {
View1(text: $view1Text)
} else {
View2(text: $view2Text)
}
Button(action: {
self.isView1Active.toggle()
}, label: {
Text("TAP")
})
}
}
}
struct View1: View {
#Binding var text: String
var body: some View {
TextField("View1: type something...", text: $text)
}
}
struct View2: View {
#Binding var text: String
var body: some View {
TextField("View2: type something...", text: $text)
}
}
Shown in action:
It view1 and view2 are completely independent and enclosure, like there is no contextmenuor sheet, you may use ZStack and opacity combinations.
var body: some View {
VStack {
ZStack{
if isView1Active {
view1.opacity(1)
view2.opacity(0)
} else {
view1.opacity(0)
view2.opacity(1)
}}
Button(action: {
self.isView1Active.toggle()
}, label: {
Text("TAP")
})
}
}

iOS SwiftUI: pop or dismiss view programmatically

I couldn't find any reference about any ways to make a pop or a dismiss programmatically of my presented view with SwiftUI.
Seems to me that the only way is to use the already integrated slide dow action for the modal(and what/how if I want to disable this feature?), and the back button for the navigation stack.
Does anyone know a solution?
Do you know if this is a bug or it will stays like this?
This example uses the new environment var documented in the Beta 5 Release Notes, which was using a value property. It was changed in a later beta to use a wrappedValue property. This example is now current for the GM version. This exact same concept works to dismiss Modal views presented with the .sheet modifier.
import SwiftUI
struct DetailView: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
Button(
"Here is Detail View. Tap to go back.",
action: { self.presentationMode.wrappedValue.dismiss() }
)
}
}
struct RootView: View {
var body: some View {
VStack {
NavigationLink(destination: DetailView())
{ Text("I am Root. Tap for Detail View.") }
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
RootView()
}
}
}
SwiftUI Xcode Beta 5
First, declare the #Environment which has a dismiss method which you can use anywhere to dismiss the view.
import SwiftUI
struct GameView: View {
#Environment(\.presentationMode) var presentation
var body: some View {
Button("Done") {
self.presentation.wrappedValue.dismiss()
}
}
}
iOS 15+
Starting from iOS 15 we can use a new #Environment(\.dismiss):
struct SheetView: View {
#Environment(\.dismiss) var dismiss
var body: some View {
NavigationView {
Text("Sheet")
.toolbar {
Button("Done") {
dismiss()
}
}
}
}
}
(There's no more need to use presentationMode.wrappedValue.dismiss().)
Useful links:
DismissAction
There is now a way to programmatically pop in a NavigationView, if you would like. This is in beta 5. Notice that you don't need the back button. You could programmatically trigger the showSelf property in the DetailView any way you like. And you don't have to display the "Push" text in the master. That could be an EmptyView(), thereby creating an invisible segue.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
MasterView()
}
}
}
struct MasterView: View {
#State private var showDetail = false
var body: some View {
VStack {
NavigationLink(destination: DetailView(showSelf: $showDetail), isActive: $showDetail) {
Text("Push")
}
}
}
}
struct DetailView: View {
#Binding var showSelf: Bool
var body: some View {
Button(action: {
self.showSelf = false
}) {
Text("Pop")
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
I recently created an open source project called swiftui-navigation-stack (https://github.com/biobeats/swiftui-navigation-stack) that contains the NavigationStackView, an alternative navigation stack for SwiftUI. It offers several features described in the readme of the repo. For example, you can easily push and pop views programmatically. I'll show you how to do that with a simple example:
First of all embed your hierarchy in a NavigationStackVew:
struct RootView: View {
var body: some View {
NavigationStackView {
View1()
}
}
}
NavigationStackView gives your hierarchy access to a useful environment object called NavigationStack. You can use it to, for instance, pop views programmatically as asked in the question above:
struct View1: View {
var body: some View {
ZStack {
Color.yellow.edgesIgnoringSafeArea(.all)
VStack {
Text("VIEW 1")
Spacer()
PushView(destination: View2()) {
Text("PUSH TO VIEW 2")
}
}
}
}
}
struct View2: View {
#EnvironmentObject var navStack: NavigationStack
var body: some View {
ZStack {
Color.green.edgesIgnoringSafeArea(.all)
VStack {
Text("VIEW 2")
Spacer()
Button(action: {
self.navStack.pop()
}, label: {
Text("PROGRAMMATICALLY POP TO VIEW 1")
})
}
}
}
}
In this example I use the PushView to trigger the push navigation with a tap. Then, in the View2 I use the environment object to programmatically come back.
Here is the complete example:
import SwiftUI
import NavigationStack
struct RootView: View {
var body: some View {
NavigationStackView {
View1()
}
}
}
struct View1: View {
var body: some View {
ZStack {
Color.yellow.edgesIgnoringSafeArea(.all)
VStack {
Text("VIEW 1")
Spacer()
PushView(destination: View2()) {
Text("PUSH TO VIEW 2")
}
}
}
}
}
struct View2: View {
#EnvironmentObject var navStack: NavigationStack
var body: some View {
ZStack {
Color.green.edgesIgnoringSafeArea(.all)
VStack {
Text("VIEW 2")
Spacer()
Button(action: {
self.navStack.pop()
}, label: {
Text("PROGRAMMATICALLY POP TO VIEW 1")
})
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
RootView()
}
}
the result is:
Alternatively, if you don't want to do it programatically from a button, you can emit from the view model whenever you need to pop.
Subscribe to a #Published that changes the value whenever the saving is done.
struct ContentView: View {
#ObservedObject var viewModel: ContentViewModel
#Environment(\.presentationMode) var presentationMode
init(viewModel: ContentViewModel) {
self.viewModel = viewModel
}
var body: some View {
Form {
TextField("Name", text: $viewModel.name)
.textContentType(.name)
}
.onAppear {
self.viewModel.cancellable = self.viewModel
.$saved
.sink(receiveValue: { saved in
guard saved else { return }
self.presentationMode.wrappedValue.dismiss()
}
)
}
}
}
class ContentViewModel: ObservableObject {
#Published var saved = false // This can store any value.
#Published var name = ""
var cancellable: AnyCancellable? // You can use a cancellable set if you have multiple observers.
func onSave() {
// Do the save.
// Emit the new value.
saved = true
}
}
Please check Following Code it's so simple.
FirstView
struct StartUpVC: View {
#State var selection: Int? = nil
var body: some View {
NavigationView{
NavigationLink(destination: LoginView().hiddenNavigationBarStyle(), tag: 1, selection: $selection) {
Button(action: {
print("Signup tapped")
self.selection = 1
}) {
HStack {
Spacer()
Text("Sign up")
Spacer()
}
}
}
}
}
SecondView
struct LoginView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView{
Button(action: {
print("Login tapped")
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image("Back")
.resizable()
.frame(width: 20, height: 20)
.padding(.leading, 20)
}
}
}
}
}
You can try using a custom view and a Transition.
Here's a custom modal.
struct ModalView<Content>: View where Content: View {
#Binding var isShowing: Bool
var content: () -> Content
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .center) {
if (!self.isShowing) {
self.content()
}
if (self.isShowing) {
self.content()
.disabled(true)
.blur(radius: 3)
VStack {
Text("Modal")
}
.frame(width: geometry.size.width / 2,
height: geometry.size.height / 5)
.background(Color.secondary.colorInvert())
.foregroundColor(Color.primary)
.cornerRadius(20)
.transition(.moveAndFade) // associated transition to the modal view
}
}
}
}
}
I reused the Transition.moveAndFade from the Animation Views and Transition tutorial.
It is defined like this:
extension AnyTransition {
static var moveAndFade: AnyTransition {
let insertion = AnyTransition.move(edge: .trailing)
.combined(with: .opacity)
let removal = AnyTransition.scale()
.combined(with: .opacity)
return .asymmetric(insertion: insertion, removal: removal)
}
}
You can test it - in the simulator, not in the preview - like this:
struct ContentView: View {
#State var isShowingModal: Bool = false
func toggleModal() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
withAnimation {
self.isShowingModal = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
withAnimation {
self.isShowingModal = false
}
}
}
}
var body: some View {
ModalView(isShowing: $isShowingModal) {
NavigationView {
List(["1", "2", "3", "4", "5"].identified(by: \.self)) { row in
Text(row)
}.navigationBarTitle(Text("A List"), displayMode: .large)
}.onAppear { self.toggleModal() }
}
}
}
Thanks to that transition, you will see the modal sliding in from the trailing edge, and the it will zoom and fade out when it is dismissed.
The core concept of SwiftUI is to watch over the data flow.
You have to use a #State variable and mutate the value of this variable to control popping and dismissal.
struct MyView: View {
#State
var showsUp = false
var body: some View {
Button(action: { self.showsUp.toggle() }) {
Text("Pop")
}
.presentation(
showsUp ? Modal(
Button(action: { self.showsUp.toggle() }) {
Text("Dismiss")
}
) : nil
)
}
}
I experienced a compiler issue trying to call value on the presentationMode binding. Changing the property to wrappedValue fixed the issue for me. I'm assuming value -> wrappedValue is a language update. I think this note would be more appropriate as a comment on Chuck H's answer but don't have enough rep points to comment, I also suggested this change as and edit but my edit was rejected as being more appropriate as a comment or answer.
This will also dismiss the view
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
let window = windowScene?.windows.first
window?.rootViewController?.dismiss(animated: true, completion: {
print("dismissed")
})

Resources