I just wondered how you could make a LazyVGrid where every item takes only the place it needs, not less and not more.
I know about .flexible() but the problem is: My Items are different sized, that means I don't know how many of them will fit in a row.
Do you got any ideas?
Thanks for your help!
Boothosh
EDIT:
LazyVGrid(columns: [GridItem(.flexible())]) {
Color.blue
.frame(width: 200, height: 200)
Color.blue
.frame(width: 100, height: 100)
Color.blue
.frame(width: 100, height: 100)
}
This is a example what Im talking about. I want to achieve that this items are placed with evenly space around them. (Not below each other, like they are now)
You just need to specify what you want by using variables.
Try this :
struct ContentView: View {
let data = (1...100).map { "Item \($0)" }
let columns = [
// The number of grid Items here represent the number of columns you will
//see. so you can specify how many items in a row thee are .
// 2 grid Items = 2 items in a row
GridItem(.flexible()),
GridItem(.flexible()),
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
Related
I have a chart showing a 10 day forecast using weatherkit, I want to show min and max temp per day so I use a BarMark with two values in the x axis. I want to show annotation for the min and one for the max. but somehow some of them appear double with different values.
is this an Apple issue or my code issue?
struct TenDayForecastViewChart: View {
let dayWeatherList: [DayWeather]
var body: some View {
VStack(alignment: .leading) {
Text("10-DAY FORECAST")
.font(.caption)
.opacity(0.5)
.padding()
Chart{
ForEach(dayWeatherList, id: \.date) { dailyWeather in
BarMark(xStart: .value("Temperature", dailyWeather.lowTemperature.converted(to: .fahrenheit).value),
xEnd: .value("Temperature", dailyWeather.highTemperature.converted(to: .fahrenheit).value),
y: .value("Day", dailyWeather.date.formatAsAbbreviatedDay())
)
.foregroundStyle(Color.black)
.annotation(position: .overlay, alignment: .leading) {
HStack {
//Image(systemName: "\(dailyWeather.symbolName)").foregroundColor(.white)
Text("\(dailyWeather.lowTemperature.converted(to: .fahrenheit).value, format: .number.precision(.fractionLength(0)))")
.foregroundColor(.white)
}
}
.annotation(position: .overlay, alignment: .trailing) {
Text("\(dailyWeather.highTemperature.converted(to: .fahrenheit).value, format: .number.precision(.fractionLength(0)))")
.foregroundColor(.blue)
}
}
}
//.chartLegend(position: .top, alignment: .bottomTrailing)
.chartXAxis(.hidden)
//.chartYAxis(.hidden)
.frame(height: 250)
.padding(.horizontal)
}
}
}
I suppose in your 10 days you have 2x "Sun", "Mon", "Tue" each. So the y plot value for these data points is identical, and by default BarCharts adds those together.
You can pass the full Date (not only the day String) into the BarChart and use
.value("Day", dailyWeather.date, unit: .day)
and add a custom formatting in .chartYAxis:
.chartYAxis {
AxisMarks(values: .stride(by: .day)) { _ in
AxisGridLine()
AxisTick()
AxisValueLabel(format: .dateTime.weekday(.abbreviated), centered: true)
}
}
I know you can change the font size dynamically with the .minimumScaleFactor view modifier however the issue is that I have 3 textfields in a VStack and I want them to all share the same dynamic font size.
If you run my example down below and just tap anywhere you will see that it either works as intended or sometimes 1 of them ends up smaller or larger than the other two. I can't tell if this is a bug or am I missing something here?
struct ContentView: View {
#State private var text = "Tap the screen!"
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(text)
Spacer(minLength: 20)
Text(text)
Spacer(minLength: 20)
Text(text)
Spacer(minLength: 20)
}
.lineLimit(7)
.font(.system(size: 100))
.minimumScaleFactor(0.01)
Spacer(minLength: 0)
}
.padding()
.contentShape(Rectangle())
.frame(width: 1600/2.5, height: 900/2.5)
.onTapGesture {
generateNewText()
}
}
private func generateNewText() {
var verse = ""
for _ in 0..<Int.random(in: 20...50) {
verse+="example "
}
text = verse
}
}
I want the font size to be the same for all three regardless how many or few words each line has.
But sometimes one of them is larger or smaller (larger in this case) than the other two and I need to prevent it.
I'm looking for a similar way https://github.com/stokatyan/ScrollCounter in SwiftUI
This is a very rudimentary build of the same thing. I'm not sure if you're wanting to do it on a per-digit basis, however this should give you a solid foundation to work off of. The way that I'm handling it is by using a geometry reader. You should be able to easily implement this view by utilizing an HStack for extra digits/decimals. The next thing I would do would be to create an extension that handles returning the views based on the string representation of your numeric value. Then that string is passed as an array and views created for each index in the array, returning a digit flipping view. You'd then have properties that are having their state observed, and change as needed. You can also attach an .opacity(...) modifier to give it that faded in/out look, then multiply the opacity * n where n is the animation duration.
In this example you can simply tie your Digit value to the previewedNumber and it should take over from there.
struct TestView: View {
#State var previewedNumber = 0;
var body: some View {
ZStack(alignment:.bottomTrailing) {
GeometryReader { reader in
VStack {
ForEach((0...9).reversed(), id: \.self) { i in
Text("\(i)")
.font(.system(size: 100))
.fontWeight(.bold)
.frame(width: reader.size.width, height: reader.size.height)
.foregroundColor(Color.white)
.offset(y: reader.size.height * CGFloat(previewedNumber))
.animation(.linear(duration: 0.2))
}
}.frame(width: reader.size.width, height: reader.size.height, alignment: .bottom)
}
.background(Color.black)
Button(action: {
withAnimation {
previewedNumber += 1
if (previewedNumber > 9) {
previewedNumber = 0
}
}
}, label: {
Text("Go To Next")
}).padding()
}
}
}
Okay, I know SwiftUI is a shift in thinking, especially coming from a world of HTML and css. But I've spent like 4 days trying to get something to work that I feel should be pretty easy and just can't so please help!
I have an app where one screen is a table of results, dynamic data that could be one or two rows/columns but could also be hundreds. So I want to be able to scroll around in cases where the table is huge.
I've replicated my setup and reproduced my problems in a Swift playground like so
import Foundation
import UIKit
import PlaygroundSupport
import SwiftUI
struct ContentView : View {
var cellSize: CGFloat = 50
var numRows: Int = 3
var numCols: Int = 3
var body : some View {
ZStack {
ScrollView([.horizontal,.vertical]) {
HStack( spacing: 0) {
VStack ( spacing: 0) {
ForEach(0 ..< numRows, id: \.self) { row in
Text("row " + row.description)
.frame( height: self.cellSize )
}
}
ForEach(0 ..< self.numCols, id: \.self) { col in
VStack( spacing: 0) {
ForEach(0 ..< self.numRows, id: \.self) { row in
Rectangle()
.stroke(Color.blue)
.frame( width: self.cellSize, height: self.cellSize )
}
}
}
}
.frame( alignment: .topLeading)
}
}
}
}
let viewController = UIHostingController(rootView: ContentView())
PlaygroundPage.current.liveView = viewController
Here's what I get when the grid is 3x3
I know things like to center by default in SwiftUI, but why isn't that .frame( alignment: .topLeading) on the HStack causing the table to be aligned to the upper left corner of the screen?
Then even worse, once that table gets large, here's what I get:
Still not aligned to the top left, which would make sense as a starting point.
When I scroll left, I can't even get to the point where I can see my header column
The view bounces me away from the edges when I get close. Like I can get to the point where I can see the top edge of the table, but it bounces me back right away.
A ton of whitespace to the right, which probably correlates to me not seeing my header columns on the left.
What am I doing wrong here? I'm exhausted trying all different frame and alignment options on various Views in here.
I have a list that loads from a parsed CSV file built using SwiftUI and I can't seem to find a way to scroll the list horizontally.
List {
// Read each row of the array and return it as arrayRow
ForEach(arrayToUpload, id: \.self) { arrayRow in
HStack {
// Read each column of the array (Requires the count of the number of columns from the parsed CSV file - itemsInArray)
ForEach(0..<self.itemsInArray) { itemNumber in
Text(arrayRow[itemNumber])
.fixedSize()
.frame(width: 100, alignment: .leading)
}
}
}
}
.frame(minWidth: 1125, maxWidth: 1125, minHeight: 300, maxHeight: 300)
.border(Color.black)
The list renders how I would like but I'm just stuck on this one point.
Preview Image Of Layout
Swift 5;
iOS 13.4
You should use an ScrollView as Vyacheslav Pukhanov suggested but in your case the scrollView size does not get updated after the async call data arrive. So you have 2 options:
Provide a default value or an alternative view.
Provide a fixed size to the HStack inside of the ForeEach. (I used this one)
I faced the same problem laying out an horizontal grid of two columns. Here's my solution
import SwiftUI
struct ReviewGrid: View {
#ObservedObject private var reviewListViewModel: ReviewListViewModel
init(movieId: Int) {
reviewListViewModel = ReviewListViewModel(movieId: movieId)
//ReviewListViewModel will request all reviews for the given movie id
}
var body: some View {
let chunkedReviews = reviewListViewModel.reviews.chunked(into: 2)
// After the API call arrive chunkedReviews will get somethig like this => [[review1, review2],[review3, review4],[review5, review6],[review7, review8],[review9]]
return ScrollView (.horizontal) {
HStack {
ForEach(0..<chunkedReviews.count, id: \.self) { index in
VStack {
ForEach(chunkedReviews[index], id: \.id) { review in
Text("*\(review.body)*").padding().font(.title)
}
}
}
}
.frame(height: 200, alignment: .center)
.background(Color.red)
}
}
}
This is a dummy example don't expect a fancy view ;)
I hope it helps you.
You should use a horizontal ScrollView instead of the List for this purpose.
ScrollView(.horizontal) {
VStack {
ForEach(arrayToUpload, id: \.self) { arrayRow in
HStack {
ForEach(0..<self.itemsInArray) { itemNumber in
Text(arrayRow[itemNumber])
.fixedSize()
.frame(width: 100, alignment: .leading)
}
}
}
}
}