I have a TabView which I made vertical by rotating, and a ScrollView inside it.
VTabView code sample here.
My ContentView is something like this:
struct ContentView: View {
// Omitted properties
var body: some View {
GeometryReader { geo in
VTabView(selection: $page) {
FirstPageView()
.frame(width: geo.size.width, height: geo.size.height)
.tag(0)
SecondPageView()
.frame(width: geo.size.width, height: geo.size.height)
.tag(1)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
}
And in my SecondPageView:
struct SecondPageView: View {
// Omitted properties
var body: some View {
GeometryReader { geo in
VStack(alignment: .center, spacing: 30) {
Image(systemName: "chevron.compact.down")
.resizable()
.scaledToFit()
.frame(height: 50)
.foregroundColor(.white.opacity(0.5))
ScrollView {
// My ScrollView contents
}
}
.padding()
}
}
}
When I'm on the second page and trying to scroll back to the first page, I can do it only outside the ScrollView's bounds. If I scroll up in ScrollView's bounds, it would stuck when ScrollView reached the top.
So how can I make VTabView's scroll gesture work when ScrollView reached the top?
Related
Here is an example of what i currenly have:
struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
VStack() {
Text("Foo")
}
.frame(maxWidth: .infinity, idealHeight: 200)
.background(Color(uiColor: .systemBackground))
}
.background(Color(uiColor: .secondarySystemBackground))
.navigationTitle("Title")
}
}
}
Here is what it renders:
What i want to achieve is the white background all the way up to the top of the screen while preserving the padding so that the actual content of the VStack is displayed below the title and not underneath it and when scroll happens VStack's content follows the title when it goes from large to inline.
Alright, i found a way to do that. It's hacky and most probably won't work properly for all screen sizes or when changing orientation of the device but i recon it would work for most iPhone models in portrait mode.
The idea is to place a ZStack inside ScrollView and put my VStack there along with a Rectangle of required color and then offset the Rectangle up to occupy the space underneath the navigation bar and more to keep the color the same when scroll spring effect happens.
Here it the code:
struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
ZStack(alignment: .top) {
Rectangle()
.foregroundColor(Color(uiColor: .systemBackground))
.frame(height: UIScreen.main.bounds.height * 0.5)
.offset(y: -UIScreen.main.bounds.height * 0.5)
.padding(.bottom, -UIScreen.main.bounds.height * 0.5)
VStack() {
Text("Foo")
}
.frame(maxWidth: .infinity)
.frame(height: 200)
.background(Color(uiColor: .systemBackground))
}
}
.background(Color(uiColor: .secondarySystemBackground))
.navigationTitle("Title")
}
}
}
Note: don't forget to set alignment: .top to ZStack!
I have a horizontal scrollview(ForEach) and vertical scrollview(ForEach) in a VStack when I scroll items from vertical scrollview(ForEach) and tap on the items from horizontal view then the tap gesture from horizontal view gets executed instead of tap gesture from vertical view.
here is my code :
//
// TestView.swift
// iOS
//
//
import SwiftUI
struct TestView: View {
#State var searchTerm = ""
var body: some View {
VStack {
TextField("Search",text: $searchTerm)
.padding()
.background(
RoundedRectangle(cornerRadius:15).fill(Color.yellow)
)
.padding()
ScrollView(.horizontal){
HStack{
ForEach(1..<100){ index in
VStack {
Image(systemName: "swift")
.resizable()
.scaledToFit()
.padding()
.frame(width: 100, height: 100, alignment: .center)
.background(Color.gray)
.clipShape(Circle())
Text("item \(index)")
}
.onTapGesture{
print("horizontal list item tapped")
}
}
}
}
ScrollView {
VStack {
ForEach(1..<100) { index in
HStack {
Image(systemName: "swift")
.resizable()
.scaledToFit()
.frame(width: 50, height: 50, alignment: .center)
Text("vertical list item : \(index)")
Spacer()
}
.contentShape(Rectangle())
.onTapGesture{
print("vertical list item \(index) tapped")
}
}
.padding(.all,5)
.background(
RoundedRectangle(cornerRadius:15).fill(Color.gray)
)
.padding(.all,5)
}
}
}
.animation(.easeIn(duration: 10))
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
before scrolling
After scrolling
FYI: having list in the place of vertical scroll view fixed the tap gestures but I would like to have scrollview because I want to use ScrollViewReader to scroll vertical list items as next step
The solution is to add clipped content shape for both scrollviews, like
ScrollView(.horizontal){
// .. content here
}.contentShape(Rectangle())
.clipped()
ScrollView {
// .. content here
}.contentShape(Rectangle())
.clipped()
Tested with Xcode 12 / iOS 14
I am trying to change background color main this view but unable to do it. I tried to put background(Color.green) at HStack, VSTack and even on ZStack but it did not work, not sure if i am putting at right place. By default it is taking phone or simulator color which is white but i want to apply custom background color
My Xcode version is 11.5
struct HomePageView: View {
#State var size = UIScreen.main.bounds.width / 1.6
var body: some View {
GeometryReader{_ in
VStack {
HStack {
ZStack{
// main home page components here....
NavigationView{
VStack {
AssignmentDaysView()
}.background(Color.lairBackgroundGray)
.frame(width: 100, height: 100, alignment: .top)
.navigationBarItems(leading: Button(action: {
self.size = 10
}, label: {
Image("menu")
.resizable()
.frame(width: 30, height: 30)
}).foregroundColor(.appHeadingColor), trailing:
Button(action: {
print("profile is pressed")
}) {
HStack {
NavigationLink(destination: ProfileView()) {
LinearGradient.lairHorizontalDark
.frame(width: 30, height: 30)
.mask(
Image(systemName: "person.crop.circle")
.resizable()
.scaledToFit()
)
}
}
}
).navigationBarTitle("Home", displayMode: .inline)
}
HStack{
menu(size: self.$size)
.cornerRadius(20)
.padding(.leading, -self.size)
.offset(x: -self.size)
Spacer()
}
Spacer()
}.animation(.spring()).background(Color.lairBackgroundGray)
Spacer()
}
}
}
}
}
struct HomePageView_Previews: PreviewProvider {
static var previews: some View {
HomePageView()
}
}
In your NavigationView you have a VStack. Instead you can use a ZStack and add a background below your VStack.
Try the following:
NavigationView {
ZStack {
Color.green // <- or any other Color/Gradient/View you want
.edgesIgnoringSafeArea(.all) // <- optionally, if you want to cover the whole screen
VStack {
Text("assignments")
}
.background(Color.gray)
.frame(width: 100, height: 100, alignment: .top)
}
}
Note: you use many stacks wrapped in a GeometryReader which you don't use. Consider simplifying your View by removing unnecessary stacks. Also you may not need a GeometryReader if you use UIScreen.main.bounds (however, GeometryReader is preferred in SwiftUI).
Try removing some layers: you can start with removing the top ones: GeometryReader, VStack, HStack...
Try the following:
Change the view background color especially safe area also
struct SignUpView: View {
var body: some View {
ZStack {
Color.blue //background color
}.edgesIgnoringSafeArea(.all)
I had a layout that essentially looked like this:
ZStack(alignment: .bottom) {
GeometryReader { geometry in
ZStack {
Text("Centered")
}
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
.background(Color.red)
}
Group {
GeometryReader { geometry in // This GeometryReader is causing issues.
VStack {
Text("I want this at the bottom")
}
.frame(width: geometry.size.width, height: nil, alignment: .topLeading)
}
}
}
When this is rendered, both Text elements are rendered in the center of the screen. The second text element's container takes up the entire width of the screen, which is intended. If I remove the problematic GeometryReader, then the text is properly rendered at the bottom of the screen, but obviously the frame is not set to the entire width of the screen. Why is this happening?
By default SwiftUI containers tight to content, but GeometryReader consumes maximum of available space. So if to remove second GeometryReader the VStack just wraps internal Text.
If it is still needed to keep second GeometryReader (to read width) and put text to the bottom, the simplest approach would be to add Spacer as below
Group {
GeometryReader { geometry in
VStack {
Spacer()
Text("I want this at the bottom")
}
.frame(width: geometry.size.width, height: nil, alignment: .topLeading)
}
}
Alternate approach of how to stick view at bottom you can find in my answer in this post Position view bottom without using a spacer
How about this?
struct ContentView: View {
var body: some View {
ZStack(alignment: .bottom) {
GeometryReader {geometry in
Text("Centered")
.frame(width: geometry.size.width, height: geometry.size.height)
.background(Color.red)
}
WidthReader {w in
Text("I want this at the bottom").frame(width: w)
}
}
}
}
struct WidthReader<Content: View>: View {
let widthContent: (CGFloat) -> Content
#State private var width: CGFloat = 0
#State private var height: CGFloat = 0
var body: some View {
GeometryReader {g in
widthContent(width).background(
GeometryReader {g1 in
Spacer().onAppear {height = g1.size.height}.onChange(of: g1.size.height, perform: {height = $0})
}
).onAppear {width = g.size.width}.onChange(of: g.size.width, perform: {width = $0})
}.frame(height: height)
}
}
The easiest way is to add the .fixedSize() modifier to your Stack.
I want to make a horizontal stack of images.
Unfortunately I am not able to slide to see full images.
struct ContentView: View {
var body: some View {
NavigationView {
List {
ScrollView {
VStack{
Text("Images").font(.title)
HStack {
Image("hike")
Image("hike")
Image("hike")
Image("hike")
}
}
}.frame(height: 200)
}
}
}
}
There are a couple of issues with your view.
You have got a List around your content - it causes problems because a list scrolls vertically, while I assume you want your images to scroll horizontally.
Next thing is that you probably don't want your title to scroll with the images - it needs to go outside the scroll view.
Last but not least, you need to make the images resizable and set their aspect ratio so that they are scaled down to fit the allocated space.
Try this:
struct ContentView: View {
var body: some View {
NavigationView {
VStack{
Text("Images").font(.title)
ScrollView(.horizontal) {
HStack {
Image("hike")
.resizable()
.aspectRatio(contentMode: .fit)
Image("hike")
.resizable()
.aspectRatio(contentMode: .fit)
Image("hike")
.resizable()
.aspectRatio(contentMode: .fit)
Image("hike")
.resizable()
.aspectRatio(contentMode: .fit)
} .frame(height: 200)
Spacer()
}
}
}
}
}