Full Screen View not working inside HStack and Horizontal ScrollView - ios

I have been trying to create onboarding flow using horizontal lists etc. I have created a view called OnboardingViewand inside it I have a VStack with image, and two Text views.
struct OnboardingView: View {
var body: some View {
GeometryReader { geometry in
VStack(spacing: 10) {
Spacer()
Image("1")
.resizable()
.frame(width: 300, height: 300)
.clipShape(Circle())
.padding(20)
Text("Travel the World")
.frame(width: geometry.size.width, height: 20, alignment: .center)
.font(.title)
Text("Explore the world and get to know different cultures and people from all around the world")
.lineLimit(nil)
.padding(.leading, 15)
.padding(.trailing, 15)
.font(.system(size: 16))
.frame(width: geometry.size.width, height: 50, alignment: .center)
.multilineTextAlignment(.center)
Spacer()
}.background(Color.red)
}
}
}
This is what I get with the above code:
Now I am trying to add this view inside a ScrollView, using HStack.
struct ContentView: View {
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
OnboardingView()
OnboardingView()
}
}.background(Color.black)
}
}
The result of the above code is absurd! This is what I get. How to go about fixing this? Help will be appreciated! Thanks.

One possible solution would be to set the width of the OnboardingView to geometry.size.width:
var body: some View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack {
OnboardingView()
.frame(width: geometry.size.width)
OnboardingView()
.frame(width: geometry.size.width)
}
}.background(Color.black)
}
}
Result

Related

Swift ui stacks causing gap at the top of the screen when frame should be first

In Swift ui I have a tabview and am going to my profile test page but in the simulator it displaying in the middle of the page.
var body: some View {
VStack() {
Text("Welcome")
.font(.largeTitle)
.foregroundColor(.black)
ZStack {
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(.red)
VStack {
Image("murry")
.clipShape(Circle())
.shadow(radius: 10)
.overlay(Circle()
.stroke(Color.red, lineWidth: 5))
Text("Murry GoldBerg")
HStack{
Text("Following:0").foregroundColor(.white)
Text("Followers:2").foregroundColor(.white)
Text("Kids:0").foregroundColor(.white)
}
}
.padding(20)
.multilineTextAlignment(.center)
}
.frame(width: 450, height: 250)
}
Spacer()
}
}
My Code as to how am inserting my views in the tab view not showing it all as no need just a snippet
struct ContentView: View {
var body: some View {
TabView{
HomeView() .tabItem {
Image(systemName: "house")
Text("Home")
}
ProfileView()
.tabItem {
Image(systemName: "person.circle.fill")
Text("Profile")
}
StatsView()
.tabItem {
Image(systemName: "plus").renderingMode(.original).padding()
Text("Plus")
}
}
Even on a real device it is doing the same so I no its not just the simlutor.
The reason is there is only one VStack so it will be default the main layout of the view. Default VStack if has only one view is from center view.
You just need add another VStack to cover your VStack then it will layout from top
var body: some View {
VStack {
VStack() {
Text("Welcome")
.font(.largeTitle)
.foregroundColor(.black)
ZStack {
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(.red)
VStack {
Image("murry")
.clipShape(Circle())
.shadow(radius: 10)
.overlay(Circle()
.stroke(Color.red, lineWidth: 5))
Text("Murry GoldBerg")
HStack{
Text("Following:0").foregroundColor(.white)
Text("Followers:2").foregroundColor(.white)
Text("Kids:0").foregroundColor(.white)
}
}
.padding(20)
.multilineTextAlignment(.center)
}
.frame(width: 450, height: 250)
Spacer()
}
}
}

How to create design covering back button and top bar fully with cross button above it?

I am trying to create below design using swiftUI but not able to find proper approach.
I tried but my image is not covering back button but coming below to back button
Expected design
VStack(spacing: 16) {
VStack {
ZStack(alignment: .topTrailing) {
Image("defaultImage")
.resizable()
.frame( height: 200)
Button("Cancel", action: {
print("cancel")
})
.padding(12)
}
}
}
My Design :(
You might want to do a lot of try in SwiftUI to get a custom design right. gave it a quick try for you and added comments on the code so you can understand most of what i did.
this is the quick result i got (with a random background i got on internet)
import SwiftUI
struct FirstView: View{
var body: some View{
NavigationView {
VStack{
NavigationLink(destination: BackgroundImageNav()){
Text("Go to the view")
.font(.title)
}
}
}
}
}
struct BackgroundImageNav: View {
#Environment(\.presentationMode) var presentationMode
// remplacing the back button action, (only problem is that this disable the swipe back)
var body: some View {
ZStack {
// ZStack help you having your image behind the other views
VStack {
Image("defaultImage")
.resizable()
.frame(height: 180)
.edgesIgnoringSafeArea(.all)
Spacer()
// having a spacer that push the image on the top and ignoring the safe area
}
VStack(alignment:.leading){
HStack{
Button(action:{
presentationMode.wrappedValue.dismiss()
// back button action
}){
Image(systemName:"chevron.left")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
.foregroundColor(.white)
}
Spacer()
Rectangle()
.fill(Color.white)
.frame(width: 200, height: 80, alignment: .center)
Spacer()
Button(action:{
// do something
}){
Image(systemName: "xmark")
.resizable()
.frame(width: 24, height: 24)
.foregroundColor(.white)
}
}.padding()
Text("Title")
.bold()
.font(.title)
.foregroundColor(.black)
.background(Color.white)
.padding(24)
Spacer()
}
}.navigationBarHidden(true)
// removing the bar to customize the back button
}
}
struct backgroundImage_Previews: PreviewProvider {
static var previews: some View {
FirstView()
}
}

How to adjust navigationBarTitle and Label Text alignment in SwiftUI

I just learned swiftUI and I got little trouble. I want to make navigationBarTitle and title headline alignment like this:
Image 1: I want to make my view like this
I have tried to make like below but it does not work:
struct HeaderView: View {
var body: some View {
NavigationView {
VStack {
Image("kante_training_champions_league")
.resizable()
.scaledToFill()
.frame(width: 370, height: 150)
.cornerRadius(10.0)
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.frame(width: 370)
Spacer()
}
.navigationBarTitle("Chelsea FC")
}
}
}
From my code above, I got a view like this:
Image 2: I got a view like this from my code above
Could someone help me how to get a view like I want
Try leading alignment
var body: some View {
NavigationView {
VStack(alignment: .leading) { // << here !!
// ... no changes in image
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.padding(.leading) // << here !!
.multilineTextAlignment(.leading)
}
You should add alignment to StackView. You can change alignment to .leading, .trailing or .center. It is centered by default thats why you are having the label in center.
var body: some View {
NavigationView {
VStack(alignment: .leading) {
// Your Code
}
}
}
Remove .frame(width: 370) and use .frame(maxWidth: .infinity) so that the text takes the whole width of its parent.
VStack {
Image("kante_training_champions_league")
.resizable()
.scaledToFill()
.frame(width: 370, height: 150)
.cornerRadius(10.0)
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity)
Spacer()
}

SwiftUI vertical ScrollView springs back up and doesn't show all the views within it

Im working on a project with a similar layout to this where I have a few views stacked within a VStack and a button on the bottom, all embedded in a scrollview.
as you can see in the image the scrollview springs back and doesn't show the button.
var body: some View {
VStack {
Rectangle()
.fill(Color.green)
.frame(height: 300)
Spacer()
ScrollView {
RectanglesView()
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Click me")
}
.offset(y: 50)
.frame(width: 300)
}
}.edgesIgnoringSafeArea(.all)
}
I do believe that the issue is due to the button's offset since it behaves normally if I remove it, but I don't want to lose the placement of the button.
Instead of offset, add padding, like this and within a VStack (slightly amended code below)
var body: some View {
ZStack{
VStack {
Rectangle()
.fill(Color.green)
.frame(height: 300)
Spacer()
ScrollView {
VStack{
Rectangle()
.fill(Color.red)
.frame(height: 300)
Rectangle()
.fill(Color.yellow)
.frame(height: 300)
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Click me")
}.padding() //instead of offset
.frame(width: 300)
}.frame(alignment: .leading)
}
}.edgesIgnoringSafeArea(.all)
}
}
Have you tried moving the Button out of the ScrollView()?
Here is what looks like:
struct ContentView: View {
var body: some View {
VStack {
Rectangle()
.fill(Color.green)
.frame(height: 300)
Spacer()
ScrollView(.vertical) {
Rectangle().fill(Color.red).frame(width: 400, height: 200)
Rectangle().fill(Color.blue).frame(width: 400, height: 200)
Rectangle().fill(Color.yellow).frame(width: 400, height: 200)
Rectangle().fill(Color.green).frame(width: 400, height: 200)
}
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Click me")
.font(.headline)
}
}.edgesIgnoringSafeArea(.all)
}
}

Center View horizontally in SwiftUI

How can I center horizontally a View (Image) in an HStack? I want a button to be left aligned and the image to be centered horizontally the view.
Currently I have this structure:
VStack {
HStack {
Button(action: {
print("Tapped")
}, label: {
Image("left-arrow")
.resizable()
.frame(width: 30, height: 30, alignment: .leading)
}).padding(.leading, 20)
Spacer()
Image("twitter-logo")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}
Spacer()
}
Which is giving me this:
But I want to achieve this:
You can embed two HStack's in a ZStack and place spacers accordingly for the horizontal spacing. Embed all that in a VStack with a Spacer() to have everything pushed up to the top.
struct ContentView : View {
var buttonSize: Length = 30
var body: some View {
VStack {
ZStack {
HStack {
Button(action: {
}, label: {
Image(systemName: "star")
.resizable()
.frame(width: CGFloat(30), height: CGFloat(30), alignment: .leading)
}).padding(.leading, CGFloat(20))
Spacer()
}
HStack {
Image(systemName: "star")
.resizable()
.frame(width: CGFloat(30), height: CGFloat(30), alignment: .center)
}
}
Spacer()
}
}
}
Note: In the second HStack, the image should automatically be center aligned, but if it isn't, you can place a Spacer() before and after the image.
Edit: Added the VStack and Spacer() to move everything to the top like the OP wanted.
Edit 2: Removed padding on image because it caused the image to be slightly offset from the center. Since it is in its own HStack and center-aligned, it does not need padding.
Edit 3: Thanks to #Chris Prince in the comments, I decided to make a simple NavigationBar-esque custom view that you can provide left, center, and right arguments to create the effect that the OP desired (where each set of views are aligned independently of each other):
struct CustomNavBar<Left, Center, Right>: View where Left: View, Center: View, Right: View {
let left: () -> Left
let center: () -> Center
let right: () -> Right
init(#ViewBuilder left: #escaping () -> Left, #ViewBuilder center: #escaping () -> Center, #ViewBuilder right: #escaping () -> Right) {
self.left = left
self.center = center
self.right = right
}
var body: some View {
ZStack {
HStack {
left()
Spacer()
}
center()
HStack {
Spacer()
right()
}
}
}
}
Usage:
struct ContentView: View {
let buttonSize: CGFloat = 30
var body: some View {
VStack {
CustomNavBar(left: {
Button(action: {
print("Tapped")
}, label: {
Image(systemName: "star")
.resizable()
.frame(width: self.buttonSize, height: self.buttonSize, alignment: .leading)
}).padding()
}, center: {
Image(systemName: "star")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}, right: {
HStack {
Text("Long text here")
Image(systemName: "star")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
.padding(.trailing)
}.foregroundColor(.red)
})
Spacer()
Text("Normal Content")
Spacer()
}
}
}
What's about saving button size to a property and add a negative padding to the image? And pay attention to an additional spacer after the image.
struct ContentView: View {
var buttonSize: Length = 30
var body: some View {
VStack {
HStack {
Button(action: {
print("Tapped")
}, label: {
Image(systemName: "star")
.resizable()
.frame(width: buttonSize, height: buttonSize, alignment: .leading)
})
Spacer()
Image(systemName: "star")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
.padding(.leading, -buttonSize)
Spacer()
}
Spacer()
}
}
}
The result:
Easiest way for me:
ZStack(){
HStack{
Image("star").resizable().foregroundColor(.white).frame(width: 50, height: 50)
Spacer()
}
Image("star").resizable().font(.title).foregroundColor(.white).frame(width: 50, height: 50)
}
You center the view using position property try this code
Group{ // container View
Image("twitter-logo")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}.position(x: UIScreen.main.bounds.width/2)
the right way to center the Title like navigationbar:
HStack {
Spacer()
.overlay {
HStack {
Image(systemName: "star")
Spacer()
}
}
Text("Title")
Spacer()
.overlay {
HStack {
Spacer()
Image(systemName: "star")
}
}
}
You can place the view that you want to center into a VStack and then set the alignment to center. Make sure that you also set the frame(maxWidth: .infinity) or else it will be centering your view in the VStack but the VStack might not take up the entire width of the screen so you might not get the appearance you are trying to achieve.
To make it even easier, write it as a function that extends the View object
extension View {
func centerInParentView() -> some View {
VStack(alignment: .center) {
self
}
.frame(maxWidth: .infinity)
}
}
And then you can just call it as you would a view modifier i.e.
VStack {
HStack {
Button(action: {
print("Tapped")
}, label: {
Image("left-arrow")
.resizable()
.frame(width: 30, height: 30, alignment: .leading)
}).padding(.leading, 20)
Spacer()
Image("twitter-logo")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}
Spacer()
}
.centerInParentView()
Works every time for me
I have got an alternative solution. I used a hidden Image as placeholder.
HStack {
Image("left-arrow").padding()
Spacer()
Image("twitter-logo")
Spacer()
// placeholder to keep layout symmetric
Image("left-arrow").padding().hidden()
}
Of course you can replace the Images with Buttons or other Views as you prefer.
Here is what worked for me
HStack {
Image(systemName: "star.fill")
.frame(maxWidth: .infinity, alignment: .leading)
Image(systemName: "star.fill")
.frame(maxWidth: .infinity, alignment: .center)
Text("")
.frame(maxWidth: .infinity, alignment: .trailing)
}
.foregroundColor(.yellow)
Inspired by SwiftUI - How to align elements in left, center, and right within HStack?
Let me propose a different solution:
https://gist.github.com/buscarini/122516641cd0ee275dd367786ff2a736
It can be used like this:
HStack {
Color.red
.frame(width: 0, height: 50)
.layoutPriority(1)
GlobalHCenteringView {
Text("Hello, world!")
.lineLimit(1)
.background(Color.green)
}
.background(Color.yellow)
Color.red
.frame(width: 180, height: 50)
.layoutPriority(1)
}
}
This will center the child view in the screen if it fits, or leave it as is if it doesn't. It is currently using UIScreen, so it only works on iOS, but you could easily pass the screen or parent width to the constructor of the view, getting it from a GeometryReader or whatever.

Resources