How to set content to Top in ScrollView? - ios

I am trying to set the content Text("Test.....") inside the scroll view to the top. Without using .frame (maxHeight: 150) and the like. I just need to kick it up under Text ("123")) but nothing comes out. And while scrolling in all directions should work. This is just an example to understand how to do it technically. Guys who can help!
import SwiftUI
struct MainView: View {
var body: some View {
NavigationView {
content
}
.padding()
}
}
private extension MainView {
var content: some View {
VStack {
ZStack(alignment: .topLeading) {
ScrollView([.vertical, .horizontal]) {
Text("TestTestTestTestTestTestTestTestTestTestTest")// set to top in content
.frame(height: 100)
.background(Color.green)
//.position(y: 0)
}
Text("123")
}
.background(Color.blue)
}
}
}

You can use .offset() for that
.offset(y: -230)
or
.offset(y: UIScreen.main.bounds.height * -0.28) // for responsive

Related

How to display a button on the picture that sees the change of "screen" in SwiftUI

Please tell me how to put a button on top of the picture (picture below), it should be located in the lower right corner of itself. And this button should lead to another screen (navigationview). Thanks in advance !
For a long time I tried to figure it out on my own, but nothing worked.
You can take reference from this code.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack{
Button (action: {})
{
VStack{
ZStack{
VStack{
Image("image")
.resizable()
.overlay {
Button {
// any action
} label: {
Text("Let's Get acquainted")
.font(.system(size: 50))
.foregroundColor(.white)
.bold()
.position(x: 150, y: 100)
}
}
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You need to use either a ZStack or .overlay:
Using ZStack:
ZStack(alignment: .bottomTrailing) {
//your content
//your image
}
Using .overlay:
//your content
.overlay(alignment: .bottomTrailing) {
//your Button
}

SwiftUI onTapGuesture not working using offset inside ZStack?

I have a Mapbox map view and a search bar and activate button (HStack) inside a ZStack and using an offset modifier to position them at the top of the screen.
For some reason the offset is preventing the onTapGesture from working for the activate button, if I comment out the offset it will work but will be placed at the bottom of the screen.
I tried adding the offset to each element individually inside the HStack but that did not work...
How can I make the onTapGesture functionality work with offset?
Thank you
struct MapScreen: View {
var body: some View {
NavigationView {
ZStack(alignment: .bottom) {
HStack(spacing: 10) {
SearchBar()
.padding(.leading, 5)
.onTapGesture {
print("search pressed")
}
ActivateButton()
.onTapGesture {
print("activate pressed")
}
}.zIndex(2)
.offset(y: -770)
MapView(locations: $locations)
.edgesIgnoringSafeArea([.top, .bottom])
BottomNavBar().zIndex(1)
.edgesIgnoringSafeArea(.all).offset(y: 35)
}
.navigationViewStyle(StackNavigationViewStyle())
.fullScreenCover(isPresented: $presentSearchView, content: {
SearchView()
})
}
}
}
There's a couple problems here:
.offset(y: -770)
If you're trying to use an offset so large, you shouldn't be using offset at all. offset is usually for fine-tune adjustments and doesn't work great with big values. And also, 770 is hardcoded. What happens when you use another device with a different screen size? Don't hardcode or do calculations yourself — SwiftUI can do it for you!
Instead, use a VStack + Spacer() to push the search bar up.
ZStack(alignment: .bottom) {
VStack { /// here!
HStack(spacing: 10) {
SearchBar()
.padding(.leading, 5)
.onTapGesture {
print("search pressed")
}
ActivateButton()
.onTapGesture {
print("activate pressed")
}
}
Spacer() /// push the `HStack` to the top of the screen
}
.zIndex(2)
MapView(locations: $locations)
.edgesIgnoringSafeArea([.top, .bottom])
BottomNavBar().zIndex(1)
.edgesIgnoringSafeArea(.all).offset(y: 35)
}

onTapGesture not working when view is inside ScrollView?

I have simple view.
var body: some View {
NavigationView {
ZStack {
ScrollView {
GeometryReader { geometry in
CardView(geometry: geometry)
.onTapGesture {
print("Tapped")
}
}
.padding()
}
}
}
When I m tapping on the card, nothing is getting printed. However if I change scrollView to VStack for instance, I instantly get Tapped on the console. What is happening? How can I implement tap gesture on my cards which are inside scrollView?
Your problem is probably on the GeometryReader, try to move it above the scrollView instead of inside
var body: some View {
NavigationView {
ZStack {
Color("light_blue_grey")
.edgesIgnoringSafeArea(.all)
GeometryReader { geometry in
ScrollView {
Rectangle()
.foregroundColor(.blue)
.frame(width: geometry.size.width, height: 100)
.onTapGesture {
print("Tapped!")
}
}
.padding()
}
}
}
}

How to implement a horizontal table view?

I'm trying to implement a horizontal table view in swift with the following functionalities:
Images listed in a horizontal manner
Horizontal scrolling left and right
See wireframe attached.
Can someone point me in the right direction? I tried digging into UICollectionView to implement this.
- SwiftUI
This question is asked a lot and there are so many answers out there for using CollectionView. But in SwiftUI you can use a horizontal ScrollView containing a Horizontal Stack like this:
struct CardView: View {
var body: some View {
Rectangle()
.foregroundColor(.gray)
.frame(width: 100, height: 80, alignment: .center)
.cornerRadius(8)
}
}
struct ContentView: View {
var body: some View {
VStack {
HStack {
Text("Categories")
Spacer()
Button(action: {}) { Text("More") }
}.padding(16)
ScrollView(.horizontal) {
HStack(spacing: 16) {
ForEach(0...15, id: \.self) { _ in
CardView()
}
}.padding(16)
}
}
}
}
- Result
Note that you can use this inside a UITableViewCell later this month when SwiftUI officially released by Apple.

ForEach inside ScrollView doesn't take whole width

I'm trying to re-create UI of my current app using SwiftUI. And it is way more difficult than I initially though.
I wanted to achieve card-like cells with some background behind them. I found that List doesn't support that, at least yet. List is so limited - it doesn't allow you to remove cell separator.
So I moved to ForEach inside ScrollView. I guess that isn't something which should be used in production for long tables but that should work for now. The problem I have is that ForeEach view doesn't take all the width ScrollView provides. I can set .frame(...) modifier but that will require hardcoding width which I definitely don't want to do.
Any ideas how to force VStack take full width of the ScrollView? I tried to use ForeEach without VStack and it has the same issue. It seems like ScrollView (parent view) "tells" its child view (VStack) that its frame is less that actual ScrollView's frame. And based on that information child views build their layout and sizes.
Here is my current result:
And here is the code:
struct LandmarkList : View {
var body: some View {
NavigationView {
ScrollView() {
VStack {
Spacer().frame(height: 160)
ForEach(landmarkData) { landmark in
LandmarkRow(landmark: landmark).padding([.leading, .trailing], 16)
}
}.scaledToFill()
.background(Color.pink)
}
.background(Color.yellow)
.edgesIgnoringSafeArea(.all)
.navigationBarTitle(Text("Landmarks"))
}
}
}
struct LandmarkRow : View {
var landmark: Landmark
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(landmark.name).font(.title)
Text("Subtitle")
.font(.callout)
.color(.gray)
}
Spacer()
Text("5 mi")
.font(.largeTitle)
}.frame(height: 80)
.padding()
.background(Color.white)
.cornerRadius(16)
.clipped()
.shadow(radius: 2)
}
}
I've got the same issue, the only way I have found so far is to fix the ScrollView and the content view width, so that every subview you add inside the content view will be centered.
I created a simple wrapper that take the width as init parameter
struct CenteredList<Data: RandomAccessCollection, Content: View>: View where Data.Element: Identifiable {
public private(set) var width: Length
private var data: Data
private var contentBuilder: (Data.Element.IdentifiedValue) -> Content
init(
width: Length = UIScreen.main.bounds.width,
data: Data,
#ViewBuilder content: #escaping (Data.Element.IdentifiedValue) -> Content)
{
self.width = width
self.data = data
self.contentBuilder = content
}
var body: some View {
ScrollView {
VStack {
ForEach(data) { item in
return self.contentBuilder(item)
}.frame(width: width)
}
.frame(width: width)
}
.frame(width: width)
}
}
By default it takes the screen width (UIScreen.main.bounds.width).
It works just like a List view:
var body: some View {
TileList(data: 0...3) { index in
HStack {
Text("Hello world")
Text("#\(index)")
}
}
}
Its possible that the answer to this might just be wrapping your scrollView inside of a GeometryReader
Like done in the answer here -> How do I stretch a View to its parent frame with SwiftUI?

Resources