How do i set full height on a component in a scroll view? - swift

I am a web developer trying to learn SwiftUI and have some questions regarding about my issue.
So my code architecture is as follows:
ContentView.swift
ScrollView(.vertical){
Foreach(users){
user in UserCard()
}
}
UserCard.swift
VStack(){
Image()
Text("long text")
}
How do I make my UserCard to always the height of my iphone? As of right now, my UserCard sometimes overflow or cuts off too quickly depending on my Text length.
Any help would be appreciated. And any tips for a SwiftUI beginner would be great!
Thank you in advanced.

If you want it to be full screen and ignoring safe area you can set the UserCard's height to screen height like this:
VStack(){
Image()
Text("long text")
}
.frame(height: UIScreen.main.bounds.height)
And if you want it to respect safe area you could use a GeometryReader:
GeometryReader { geometry in
ScrollView {
Color.red
.frame(height: geometry.size.height)
Color.blue
.frame(height: geometry.size.height)
Color.yellow
.frame(height: geometry.size.height)
}
}

one Question -> one Answer
for second part of your Question need new Question with own Topic.
ZStack()
{
VStack()
{
Image(systemName: "star")
Text("long text")
}
}
.ignoresSafeArea()

Related

How to keep an item exactly in the center of an HStack with other items in the HStack

I have two items in an HStack. I want one of them to be perfectly horizontally centered and the other to be on the left side (have an alignment of .leading). Here is my code so far:
HStack {
Text("1")
.frame(alignment: .leading)
Text("Hello")
.frame(alignment: .center)
}
I have figured out that the .overlay method works. However, I still want Text("Hello") to be aware of the position of Text("1") so that if Text("Hello") were a longer string, it would know not to cover up Text("1"), which is exactly what happens when the .overlay function is used. So I'm wondering if there are any other solutions.
You can add one more Text view inside HStack as hidden which have the same content as your left align text view along with Spacer. Something like this.
HStack {
Text("1")
Spacer()
Text("Good Morning, Hello World. This is long message")
.multilineTextAlignment(.center)
Spacer()
Text("1") // Set the same content as left aligned text view
.hidden()
}
.padding(.horizontal)
Preview
Not sure if it's the best way, but you can use a GeometryReader to get the screen width and skip the first half.
To place the text exactly in the center, you should also calculate the width of the text. you can use a simple extension to get the size of your text:
extension String {
public func size(withFont font: UIFont) -> CGSize {
(self as NSString).size(withAttributes: [.font: font])
}
}
Here is the example code:
GeometryReader { geo in
HStack {
Spacer()
.frame(width: (geo.size.width - "center".size(withFont: UIFont.systemFont(ofSize: 18)).width) / 2)
HStack {
Text("center")
Text("another")
}
}
}

SwiftUI Nested GeometryReader - Breaking UI

So I am trying to understand why my subview (TopView) is having weird resizing issues.
Here is the sample
import SwiftUI
struct ContentView: View {
#State var isInterfaceHidden: Bool = false
var body: some View {
VStack(spacing: 0, content: {
if !isInterfaceHidden {
TopView()
.background(Color.yellow)
}
Rectangle()
.foregroundColor(Color.red)
/// We make sure it won't cover the top and bottom view.
.zIndex(-1)
if !isInterfaceHidden {
Rectangle()
.foregroundColor(Color.yellow)
.frame(height: 80)
}
})
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
struct TopView: View {var body: some View {
HStack(content: {
VStack(spacing: 0, content: {
Text("Text to show, it is a title.")
.tracking(0.2)
.foregroundColor(.white)
.lineLimit(1)
GeometryReader(content: { geometry in
Text("Text to show, it is a subline.")
.tracking(0.2)
.foregroundColor(.white)
.lineLimit(1)
})
.background(Color.purple)
})
})
.padding([.leading, .trailing], 20)
}
}
I tried to set a .fixedSize() like this:
GeometryReader(content: { geometry in
Text("Text to show, it is a subline.")
.tracking(0.2)
.foregroundColor(.white)
.lineLimit(1)
})
.background(Color.purple)
But it is not fitting the text exactly, so I am not sure if this is the right solution. Do you guys have any idea?
Be aware that GeometryReader has had what appears to be a regression as of 14.0 (26/Sep/20) - or perhaps a wonderfully undocumented change of behaviour - with weighting layouts towards the topleft corner - rather than the center.
This has only appeared with apps I developed and built with XCode 12 - an XCode-11-compiled-app running on iOS 14 did not exhibit the issue. Most tutorials on the net will be assuming this worked the way it did in iOS 13/XCode 11 and your code may function differently
iOS 14 has Changed (or broken?) SwiftUI GeometryReader for a more involved question with the same issues
As far as I know, GeometryReader passes back its parent a size that is given by the parent unless you set frame() to GeometryReader explicitly. Even so, If you want to fit the area of GeometryReader to the Text view (exactly your custom view), you will have to calculate a height of the custom view by using preference or anchorPreference and then set it as a height of GeometryReader in order to let the parent know what size it needs to assign.
I hope the following link will be helpful.
https://swiftui-lab.com/communicating-with-the-view-tree-part-1/
GeometryReader fit to View
If you're looking for the GeometryReader to not affect the size of your view, you should make an inversion. The view that you return inside the GeometryReader should be out, and the GeometryReader itself should be put in a background or in a overlay of that View.
Text("Text to show, it is a subline.")
.tracking(0.2)
.foregroundColor(.white)
.lineLimit(1)
.overlay(
GeometryReader(content: { geometry -> Color in
print(geometry.frame(in: .global))
return Color.clear
})
)
.background(Color.purple)
Either way (background or overlay), would solve your problem. Try changing overlay to background to see.
Just remember to return a Color.clear, this way, the GeometryReader becomes invisible and it doesn't change the View.

SwiftUI horizontal ScrollView wanting to auto centre item

I’m very new to SwiftUI and trying to figure out the best way to handle this.
I have set up a card view and that I have in a horizontal ScrollView. I am trying to get it to snap or align to the next card so it is always central. Think App Store apps or games tab in the header, I want it to behave like this.
I have searched around a lot for this but cant seem to find a solution, at least in SwiftUI. I implemented a similar thing in a UIKit app but I’m looking for a native SwiftUI solution if at all possible. This is what I implemented previously https://github.com/BenEmdon/CenteredCollectionView Though I am trying to seek a lighter and native SwiftUI way of doing this if at all possible as I always felt this was a little heavy for what I need.
The code I have so far is as follows:
ScrollView(.horizontal,
content: {
HStack {
CardView(card: cardDetails)
CardView(card: cardDetails)
CardView(card: cardDetails)
CardView(card: cardDetails)
}
.padding(.leading, 10)
})
.frame(height: 250)
struct CardView: View {
let card: Card
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(Color.white)
VStack {
Text(card.prompt)
.font(.largeTitle)
.foregroundColor(.black)
Text(card.answer)
.font(.title)
.foregroundColor(.gray)
}
.padding(20)
.multilineTextAlignment(.center)
}
.frame(width: 450, height: 250)
}
}
Any input will be greatly appreciated, thank you in advance for your help.
Here is an image from the App Store, the header section is similar to what I want to achieve, when you scroll through these it scrolls on a per item basis and almost snaps to the next item rather than a free scroll:

SwiftUI ScrollView dynamic Height

I currently have the problem that my scroll view does not automatically adjust to the height of the content. I think ASCollectionView is the one that causes the most problems at the moment. If I adjust the frame height manually (here e.g. 200 - see picture), then everything is visible. Of course I want to have it automatically. Do you have any idea how this could work?
I would be very happy! Have a nice afternoon.
Florian
ScrollView {
ForEach(self.collectionListViewModel.collections) { collection in
NavigationLink(destination: GameListView()) {
VStack(alignment: .leading, spacing: 5) {
// Name der Sammlung:
Text(collection.name)
.font(.headline)
.foregroundColor(Color.black)
// Optional: Für welche Konsolen bzw. Plattformen:
ASCollectionView(data: collection.platforms, dataID: \.self) { item, _ in
Text("\(item)")
.fixedSize()
.padding(.horizontal, 10)
.padding(.vertical, 5)
.background(Color(.blue))
.foregroundColor(Color.white)
.cornerRadius(5)
}
.layout {
let fl = AlignedFlowLayout()
fl.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
return fl
}
}
.padding(.vertical, 10)
}
Divider()
}
.padding(.horizontal, 20)
}
The problem here is that ASCollectionView will (by default) take up however much room it is given. When nested in a ScrollView the amount of space available is undefined. You could set up the collectionView to automatically size itself to its content. An example is given in the demo project here
However, nesting scrollviews can cause difficulty, so I think the behaviour you're after would be more easily achieved by placing the title text as a section in the ASCollectionView.

SwiftUI ScrollView only scroll in one direction

Trying to make a custom list using a view as the list row style (to get rid of the ugly line separates in the list by default).
However, once I put my ZStack rows inside a scroll view, the scroll view scrolls in both directions and not just vertically.
Here is the contentView:
NavigationView {
ScrollView{
VStack(alignment: .leading){
ForEach(friends) { friends in
NavigationButton(destination: MessageDetailView(friend: friends)) {
CustomListItem(friend: friends)
}
}
Spacer()
}
}
.foregroundColor(.black)
.navigationBarTitle(Text("Messages"))
}
and here is the customListItem:
Group{
ZStack {
RoundedRectangle(cornerRadius: 10)
.shadow(radius: 1, y:1)
.frame(width: UIScreen.main.bounds.width - 32, height: 75)
.foregroundColor(.gray)
HStack {
Image(systemName: "person.crop.circle")
.resizable()
.frame(width: 50, height: 50)
VStack(alignment: .leading) {
HStack {
Text(friend.name)
.font(.headline)
Text("\(friend.date, formatter: dateFormatter)")
}
Text(friend.messagesReceived[0])
.font(.subheadline)
} .lineLimit(nil)
Spacer()
Image(systemName: "chevron.right.circle")
.foregroundColor(.black)
}.padding(10)
}.padding([.leading, .trailing])
}
Is there any way I can limit the scrolling to vertical or force a frame on this?
Trying to use the .frame(...) modifier does not work as I've tried it. This results in the view not loading at all.
Example Images:
As of Xcode 11 beta 3 (11M362v) there is an axes parameter when constructing a ScrollView which can be set to .horizontal or .vertical
ScrollView(.vertical, showsIndicators: true) { ... }
This can be achieved with a GeometryReader. Wrap your ScrollView in one and then set the width of your VStack.
GeometryReader is a super easy, and pretty useful trick to have in your belt for SwiftUI :)
Here's how I got it working with your code:
NavigationView {
GeometryReader { geometry in
ScrollView {
VStack(alignment: .leading){
ForEach(self.friends) { friend in
NavigationButton(destination: MessageDetailView(friend: friend)) {
CustomListItem(friend: friend)
}
}
Spacer()
}.frame(width: geometry.size.width)
}
.foregroundColor(.black)
.navigationBarTitle(Text("Messages"))
}
}
Using iOS 14, Swift 5, SwiftUI 2.0
This is the answer... you can use both vertical and horizontal in a set.
ScrollView([.vertical,.horizontal]) {
....
}
Although in most cases the above answers works but none of them did work for me!
In my case, the problem was the scrollView children which were wider than the scrollView!
adding a static width to them did the trick.
Try being more specific with the scroll direction like this
ScrollView(.vertical) {}