How to prevent view from leaving screen in a ScrollView SwiftUI - swiftui

I want to prevent the 3 bars from leaving the screen. Once they have reached the top of the screen they will remain there until scrolling has been reset. So only the circle and remaining elements will continue to scroll.
If possible it would also make sense for the circle to rest as well but further off screen.
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView {
LazyVStack {
Circle()
HStack {
Rectangle()
.frame(height: 50)
.padding()
Rectangle()
.frame(height: 50)
.padding()
Rectangle()
.frame(height: 50)
.padding()
}
ForEach(0..<20) { index in
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.padding()
}
}
.padding()
}
}
}

You could turn the rectangles into a section for your LazyVStack list and then set the
stack view to pin the section headers using LazyVStack(pinnedViews:[.sectionHeaders]) Please see Apple documentation for more details.
struct ContentView: View {
var body: some View {
ScrollView {
LazyVStack(pinnedViews:[.sectionHeaders]) {
Circle()
Section(header:
HStack {
Rectangle()
.frame(height: 50)
.padding()
Rectangle()
.frame(height: 50)
.padding()
Rectangle()
.frame(height: 50)
.padding()
}) {
ForEach(0..<20) { index in
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.padding()
}
}
}
.padding()
}
}
}

Related

Utilize HStack to confirm to logo and skip text

Does anyone know how to make the following view in SwiftUI?
HStack:
[ blank logo(centered) Skip(text)]
So I have the following HStack:
Zstack(alignment: .topLeading) {
VStack{
HStack {
Image("onboarding-logo")
.resizable()
.scaledToFit()
.frame(width: 150.0, height: 150.0)
.padding(.top, 35)
}
}
}
Does anyone know how I can have a "Skip" text in the top right corner of the screen, but also keep my logo centered and not have anything on the left side? I've tried Spacers and all, but I'm having no luck.
I would like to click "Skip" and then lead to another view.
There are many ways to achieve this. Here I have used LazyVGrid since other answers based on Stacks
struct ContentView: View {
var body: some View {
VStack {
LazyVGrid(columns: [GridItem(), GridItem(), GridItem()], content: {
Spacer()
Image(systemName: "star.fill")
.frame(width: 150, height: 150, alignment: .center)
.border(.gray)
Button(action: {
}, label: {
Text("Skip")
})
})
.border(.gray)
Spacer()
}
}
}
Is this the screen configuration you want?
The Zstack is used to center the Hstack into which the image is placed, and the new HStack uses a spacer to move the Text to the right.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
VStack {
HStack(alignment: .center) {
Image("farnsworth")
.resizable()
.scaledToFit()
.frame(width: 150.0, height: 150.0)
}
Spacer()
}
VStack {
HStack {
Spacer()
Button {
// do Something...
} label: {
Text("Skip>>")
.padding(.top, 10)
}
}
Spacer()
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Scrollable content not working as expected in SwiftUI

I'm trying to create a scroll view with my custom view, but when I add scroll to view it's not working as expected, without scroll view working fine.
struct ContentView: View {
var body: some View {
ScrollView(.vertical) {
VStack {
ForEach (0..<2) { _ in
ListItem()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// But the below code is working fine.
struct ContentView: View {
var body: some View {
VStack {
ForEach (0..<2) { _ in
ListItem()
}
}
}
}
// List Item
struct ListItem: View {
var body: some View {
VStack {
HStack {
Image("steve")
.resizable()
.clipShape(Circle())
.aspectRatio(contentMode: .fit)
.frame(maxWidth:44, maxHeight: 44)
VStack {
Text("Steve Jobs")
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
Text("1 hour ago")
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
}
Spacer()
}
ZStack(alignment:.top) {
GeometryReader { geometry in
VStack {
ZStack {
Image("poster_1")
.resizable()
.aspectRatio(contentMode: .fit)
.cornerRadius(8)
.shadow(color: Color.black.opacity(0.12),
radius: 4, x: 1, y: 1)
.frame(width: geometry.size.width - 64,
height: geometry.size.height * 0.35)
.padding([.horizontal], 32)
.clipped()
ZStack {
Rectangle()
.fill(Color.black.opacity(0.75))
.frame(maxWidth:84 , maxHeight: 84)
.cornerRadius(12)
Image(systemName: "play.fill")
.font(.system(size: 44, weight: .bold))
.foregroundColor(.white)
}
}
VStack {
Text("Game of Thrones")
.accentColor(Color.gray.opacity(0.25))
.font(Font.subheadline.weight(.bold))
.padding([.horizontal], 32)
.padding([.bottom], 2)
.frame(maxWidth: .infinity,
alignment: .leading)
VStack {
Text("Game of Thrones is an American fantasy drama television series created by David Benioff and D. B. Weiss for HBO. ")
.accentColor(Color.gray.opacity(0.25))
.font(.footnote)
.frame(maxWidth: .infinity,
alignment: .leading)
.padding([.horizontal], 32)
Text("Show more...")
.accentColor(Color.gray.opacity(0.01))
.font(Font.footnote.weight(.bold))
.frame(maxWidth: .infinity,
alignment: .trailing)
.padding([.trailing], 32).onTapGesture {
print("okay")
}
}
}
}
}
}
}
}
}
ListItem contains multiple views which creates publisher info and movie information as shown below image.
Scrollview is scrolling but images are not shown in view as first image.
It is the geometry reader that you have in ListItem. Because neither a GeometryReader nor a Scrollview have their own size. Since neither no what size to render, they collapse. This is what you are seeing in your view. See this answer. The solution is to put the GeometryReader into ContentView outside the Scrollview and send the GeometryProxy that you called geometry into ListItem something like this:
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical) {
VStack {
ForEach (0..<2) { _ in
ListItem(geometry: geometry)
}
}
} // Scrollview
} // GeometryReader
}
}
struct ListItem: View {
let geometry: GeometryProxy
var body: some View {
...
}
This seems to fix it in Preview, though you may have to change your multipliers in the .frame() that uses geometry to size it how you want.

Is there a way to use Spacer() in a ZStack? (SwiftUI)

I have text on top of a rectangle through a ZStack and I was wondering if there was a way to limit the Spacer() amount within the rectangle.
ZStack{
Rectangle()
.frame(width: geometry.size.width,
height: geometry.size.height/3.25)
.shadow(radius: 5)
.foregroundColor(Color.white)
//Words ontop of the Rectangle
VStack {
HStack {
Spacer()
Text("Hello World")
}.padding(.trailing, 40)
Spacer() //<-- PROBLEM HERE
}//.offset(y: -40)
}
What It Looks Like
tl;dr:
I'd like to have is so that "Hello World", doesn't go passed the bounds of the rectangle when Spacer() is used. How can I do this? Thanks
I think what you're trying to accomplish can be achieved by just using a ZStack and specifying .topTrailing alignment:
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .topTrailing) {
Rectangle()
.frame(height: geometry.size.height/3.25)
.shadow(radius: 5)
.foregroundColor(Color.white)
Text("Hello World")
.padding(.trailing, 40)
}
.frame(maxHeight: .infinity)
}
}
}
Alternatively:
You can forego the ZStack and make the Text an .overlay() of the Rectangle(). Here I kept your VStack and just made it an overlay which keeps it from going beyond the Rectangle.
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
Rectangle()
.frame(height: geometry.size.height/3.25)
.shadow(radius: 5)
.foregroundColor(Color.white)
.overlay(
VStack {
HStack {
Spacer()
Text("Hello World")
}.padding(.trailing, 40)
Spacer()
}
)
.frame(maxHeight: .infinity)
}
}
}

full screen background image with vstack

i want to have a full screen background image with a navigationview (must be on top because it is from the basis view and not in "this" view normally).
In this view i want a VStack which is just inside the secure area, so between navigationbar and bottom layout.
unfortunately i got (see picture)
I expected the texts inside...
struct ContentView: View {
var body: some View {
NavigationView {
ZStack(alignment: .center) {
Image("laguna")
.resizable()
.edgesIgnoringSafeArea(.all)
.scaledToFill()
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
VStack(alignment: .center) {
Text("just a test")
.font(.largeTitle)
.foregroundColor(Color.white)
Spacer()
Text ("not centered....why?")
.font(.largeTitle)
.foregroundColor(Color.white)
}
.zIndex(4)
.navigationBarTitle("nav bar title")
}
}
}
}
Here is a bit modified variant. Tested with Xcode 11.4 (finally released) / iOS 13.4
struct TestFullScreenImage: View {
var body: some View {
NavigationView {
ZStack {
Image("large_image")
.resizable()
.edgesIgnoringSafeArea(.all)
.scaledToFill()
VStack {
Text("Just a test")
.font(.largeTitle)
.foregroundColor(.white)
Spacer()
Text("centered")
.font(.largeTitle)
.background(Color.green)
}
.navigationBarTitle("Navigation Title")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
If need to use NavigationView, and maintain the image's aspect ratio, you can do this:
import SwiftUI
struct FullScreenPictureDemo: View {
var body: some View {
NavigationView {
ZStack {
Image("your_full_screen_background_picture")
.resizable()
.aspectRatio(contentMode: .fill)
.edgesIgnoringSafeArea(.all)
.scaledToFill()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}

SwiftUI - Tapping a Button changes VStack's background color

When I tapped OrderButton, all the VStack's background color is changing to gray color then turning back like assigned a click event. How can I prevent this?
import SwiftUI
struct DrinkDetail : View {
var drink: Drink
var body: some View {
List {
ZStack (alignment: .bottom) {
Image(drink.imageName)
.resizable()
.aspectRatio(contentMode: .fit)
Rectangle()
.frame(height: 80)
.opacity(0.25)
.blur(radius: 10)
HStack {
VStack(alignment: .leading, spacing: 3) {
Text(drink.name)
.foregroundColor(.white)
.font(.largeTitle)
Text("It is just for $5.")
.foregroundColor(.white)
.font(.subheadline)
}
.padding(.leading)
.padding(.bottom)
Spacer()
}
}
.listRowInsets(EdgeInsets())
VStack(alignment: .leading) {
Text(drink.description)
.foregroundColor(.primary)
.font(.body)
.lineLimit(nil)
.lineSpacing(12)
HStack {
Spacer()
OrderButton()
Spacer()
}
.padding(.top, 50)
.padding(.bottom, 50)
}
.padding(.top)
}
.edgesIgnoringSafeArea(.top)
.navigationBarHidden(true)
}
}
struct OrderButton : View {
var body: some View {
Button(action: {}) {
Text("Order Now")
}
.frame(width: 200, height: 50)
.foregroundColor(.white)
.font(.headline)
.background(Color.blue)
.cornerRadius(10)
}
}
#if DEBUG
struct DrinkDetail_Previews : PreviewProvider {
static var previews: some View {
DrinkDetail(drink: LoadModule.sharedInstance.drinkData[3])
}
}
#endif
The issue resolved by using ScrollView and adding some more parameters for auto resize of Text:
ScrollView(.vertical, showsIndicators: false) {
ZStack (alignment: .bottom) {
Image(drink.imageName)
.resizable()
.aspectRatio(contentMode: .fit)
Rectangle()
.frame(height: 80)
.opacity(0.25)
.blur(radius: 10)
HStack {
VStack(alignment: .leading, spacing: 3) {
Text(drink.name)
.foregroundColor(.white)
.font(.largeTitle)
Text("It is just for $5.")
.foregroundColor(.white)
.font(.subheadline)
}
.padding(.leading)
.padding(.bottom)
Spacer()
}
}
.listRowInsets(EdgeInsets())
VStack(alignment: .leading) {
Text(drink.description)
.foregroundColor(.primary)
.font(.body)
.lineLimit(nil)
.fixedSize(horizontal: false, vertical: true)
.lineSpacing(12)
.padding(Edge.Set.leading, 15)
.padding(Edge.Set.trailing, 15)
HStack {
Spacer()
OrderButton()
Spacer()
}
.padding(.top, 50)
.padding(.bottom, 50)
}
.padding(.top)
}
.edgesIgnoringSafeArea(.top)
.navigationBarHidden(true)
What's the UI you are trying to get? Because, from your example, it seems that List is not necessary. Indeed, List is:
A container that presents rows of data arranged in a single column.
If you don't really need List you can replace it with VStack (if your content must fit the screen) or with a ScrollView (if your content is higher than the screen). Replacing the List will solve your issue that depends on the list selection style of its cells.
An alternative, if you need to use the List view is to set the selection style of the cells in the SceneDelegate to none (but this will affect all of your List in the project) this way:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView() //replace this with your view
if let windowScene = scene as? UIWindowScene {
UITableViewCell.appearance().selectionStyle = .none
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}