How do you set autosaveName for SwiftUI VSplitView - swiftui

I have a VSplitView in my app and I would like the position to be saved and restored automatically by the user defaults. Normally I would do this by providing an autosaveName for the split view either programmatically or in the XIB.
How do I accomplish that in SwiftUI using a VSplitView?
struct ContentView: View {
var body: some View {
VSplitView {
Text("Top View")
.padding()
.frame(maxWidth:.infinity, maxHeight: .infinity)
Text("Bottom View")
.padding()
.frame(maxWidth:.infinity, maxHeight: .infinity)
}
// I expected something like:
//.autosaveName("MainSplitView")
}
}

Related

Make a View the same size as another View which has a dynamic size in SwiftUI

I want to achieve this (For the screenshots I used constant heights, just to visualize what I am looking for):
The two Views containing text should together have the same height as the (blue) Image View. The right and left side should also be of the same width. The important part: The Image View can have different aspect ratios, so I can't set a fixed height for the parent View. I tried to use a GeometryReader, but without success, because when I use geometry.size.height as height, it takes up the whole screen height.
This is my code:
import SwiftUI
struct TestScreen: View {
var body: some View {
VStack {
GeometryReader { geometry in
HStack {
Image("blue-test-image")
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity)
VStack {
Text("Some Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
Text("Other Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
}
.frame(maxWidth: .infinity, maxHeight: geometry.size.height /* should instead be the height of the Image View */)
}
}
Spacer()
}
.padding()
}
}
This is a screenshot of what my code leads to:
Any help would be appreciated, thanks!
As #Asperi pointed out, this solves the problem: stackoverflow.com/a/62451599/12299030
This is how I solved it in this case:
import SwiftUI
struct TestScreen: View {
#State private var imageHeight = CGFloat.zero
var body: some View {
VStack {
HStack {
Image("blue-test-image")
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity)
.background(GeometryReader {
Color.clear.preference(
key: ViewHeightKeyTestScreen.self,
value: $0.frame(in: .local).size.height
)
})
VStack {
Text("Some Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
Text("Other Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
}
.frame(maxWidth: .infinity, minHeight: self.imageHeight, maxHeight: self.imageHeight)
}
.onPreferenceChange(ViewHeightKeyTestScreen.self) {
self.imageHeight = $0
}
Spacer()
}
.padding()
}
}
struct ViewHeightKeyTestScreen: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
struct TestScreen_Previews: PreviewProvider {
static var previews: some View {
TestScreen()
}
}

Detect if user tapped the screen, including Buttons, in SwiftUI

I've been trying to use .onTapGesture to detect if the user taps on the screen. However, I noticed that it doesn't get triggered if the user taps on actionable elements like buttons and pickers. Is there a way to detect if the user taps on the screen, including these actionable elements without having to manually call my viewTapped function for each button, picker, etc. separately?
Here's the code I used to test this.
struct ContentView: View {
#State var mode = 0
var body: some View {
ZStack {
VStack {
Text("Hello, world!")
.padding()
Picker(
selection: $mode,
label: Text("Picker")) {
Text("Option 1").tag(0)
Text("Option 2").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 200)
.foregroundColor(.white)
.padding()
Button {
print("button clicked")
} label: {
Text("Click me")
}
.padding()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.green)
.onTapGesture {
viewTapped()
}
}
public func viewTapped() {
print("view tapped")
}
}
If you need to detect touches simultaneity, you need the simultaneousGesture modifier instead. like:
.simultaneousGesture(
TapGesture()
.onEnded {
viewTapped()
}
)

PageTabViewStyle breaks NavigationView

I am working on a SwiftUI view. When using the NavigationView inside the TabView the app works as expected.
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
Text("A")
.navigationBarTitle("Nav A")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
NavigationView {
Text("B")
.navigationBarTitle("Nav B")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
//.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) // this line breaks the view
}
}
As soon as I uncomment .tabViewStyle(...) the view does not work as expected. Any ideas why? The text is not visible anymore. The navigation does not seem to be correct either.
Try moving the NavigationView outside the TabView:
struct ContentView: View {
#State var selection = 1
var body: some View {
NavigationView {
TabView(selection: $selection) {
Text("A").tag(1)
Text("B").tag(2)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.navigationTitle(selection == 1 ? "A" : "B")
}
}
}
It seems like the NavigationView defaults to presenting as a primary/detail display.
You can force it to work like a normal navigation stack with the modifier .navigationViewStyle(StackNavigationViewStyle())
Here it is in your code:
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
Text("A")
.navigationBarTitle("Nav A")
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: .infinity, maxHeight: .infinity)
NavigationView {
Text("B")
.navigationBarTitle("Nav B")
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}

How to access safeArea insets while the parent view used IgnoringSafeArea()

As you can see the picture I want to place the search bar exactly to the top of the safeArea but the proxy.safeAreaInsets has not the proper value because in the PreviewProvider the parent uses edgesIgnoringSafeArea.
what can I do ? is there any way to access safeAreaInsets?
struct FindView: View {
// MARK: - Properties
#ObservedObject var viewModel: FindViewModel
init(viewModel: FindViewModel){
self.viewModel = viewModel
}
var body: some View {
GeometryReader { proxy in
ScrollView(.vertical, showsIndicators: true, content: {
VStack(alignment: .leading, spacing: 8){
SearchBar()
.frame(height: 48, alignment: .center)
.padding(.all, 16)
Text("Categories")
.multilineTextAlignment(.leading)
.font(.system(size: 21))
.padding(.all, 16)
}.frame(width: proxy.size.width)
})
}
}
}
struct FindView_Previews: PreviewProvider {
static var previews: some View {
ZStack(alignment: .center, content: {
Rectangle().foregroundColor(.red)
FindView(viewModel: FindViewModel())
}).edgesIgnoringSafeArea(.all)
}
}
simple way;
struct test: View {
var body: some View {
VStack{
Text("Your view comes here")
Spacer()
}
.frame(minWidth:0, maxWidth: .infinity, minHeight: 0,maxHeight: .infinity, alignment: .center)
.padding(.top,UIApplication.shared.windows.first?.safeAreaInsets.top ?? 40)
.background(Color.red)
.edgesIgnoringSafeArea(.all)
}
}
You should apply the .edgesIgnoringSafeArea(.all) only to the rectangle, not to the ZStack. This way, only it should fill the screen, while everything else remains in place.
Then, to make it so that the FindView fills the screen (respecting safe area), you need make its frame extend with .frame(maxWidth: .infinity, maxHeight: .infinity)
Code sample:
struct ContentView: View {
var body: some View {
ZStack {
Rectangle()
.fill(Color.red)
.edgesIgnoringSafeArea(.all)
Text("This should respect the safe area")
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
}
}
}

How can I remove a space when I use Image in multiple stacks?

I'm currently developing an application using SwiftUI.
When I use 2 HStacks which have Images in a Vstack, the VStack makes a space between 2 HStacks like the photo below:
Then How can I remove that space?
I am confused about this problem because if I exchange Images in HStacks to Texts, that space disappears...
How can I remove that space when I use Images in HStacks?
Here is the code:
ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "circle")
// Text("test")
} .frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.yellow)
HStack{
Image(systemName: "circle")
// Text("test")
} .frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.blue)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Xcode: Version 11.7
Swift: Swift 5