Im using custom scrollview with UIViewControllerRepresentable and works fine but when I using with LazyVStack it doesn't work that I expected.
LazyVStack doesn't create items until it needs to render them onscreen But when I use this with custom scrollview, it draws at once.
So when I do this,
ScrollView {
LazyVStack {
ForEach(0...10000, id:\.self) {
Text(String($0))
}
}
}
It works well, 10,000 items are shows well without lagging.
But when I do this with my custom scrollView
CustomScrollView {
LazyVStack {
ForEach(0...10000, id:\.self) {
Text(String($0))
}
}
}
It comsumes memory as I expected.
Can't I using this with custom scrollView?
Related
Faced with a very strange ScrollView behavior on macOS. The content freezes under the mouse during horizontal scrolling. But it is worth taking the mouse away from the window and the content scrolls normally.
This happens when I try to use a vertical scroll inside a horizontal one:
struct ScrollTestView: View {
var body: some View {
ScrollView(.horizontal) {
ScrollView(.vertical) {
VStack {
ForEach(0..<20, id: \.self) { row in
HStack {
ForEach(0..<20, id: \.self) { item in
Text("\(item)")
.font(.title)
.padding()
.background {
Color.gray
}
}
}
}
}
}
}
}
}
Yes, I know that I can use the same ScrollView for both axes simultaneously, but I need solution with two ScrollViews because of desired UX.
This solution is perfectly works on iOS, but I have this strange behavior on macOS.
Also if you swap a horizontal and a vertical ScrollView in the exact same code, everything works just fine:
struct ScrollTestView: View {
var body: some View {
ScrollView(.vertical) {
ScrollView(.horizontal) {
// ...
}
}
}
}
Looks like this is a SwiftUI bug, but I am not sure, maybe I am missing something?
Any ideas?
What is the correct way to combine the mentioned views.
As of now, i have a NavigationStack at the bottom of my app. It displays a LaunchView as root. When a user is authenticated, the main view is added to the stack, if not, the login/ register views are added to the stack. This works fine.
NavigationStack(path: self.$vm.path) {
LaunchView()
.navigationDestination(for: Authentication.self) { value in
switch value {
case .login:
LoginView(vm: LoginViewModel() { type, action in
...
})
case .verify: // let .verify(email)
VerifyMailView(vm: VerifyMailViewModel() { type, action in
...
})
case .authenticated:
AuthenticatedView() { type, action in
self.vm.set(type: type) { value in
...
}
}
}
}
}
The AuthenticatedView consists of a TabView with three views. Here comes the issue. I assumed i could set the title and toolbars of the three views directly on them, which is not the case. However i don't want nor can set the titles or the toolbars within the tabview, as they require data from the views viewmodels (to which the TabView has, and should not have, no access).
TabView(selection: self.$vm.index) {
DiscoverView(vm: DiscoverViewModel(account: self.vm.$account, type: self.type))
.tabItem {
Image(self.vm.index == 0 ? "discover.selected" : "discover")
}
.tag(0)
MatchesView(vm: MatchesViewModel(type: self.type))
.tabItem {
Image(self.vm.index == 1 ? "matches.selected" : "matches")
}
.tag(1)
ProfileView(vm: ProfileViewModel(account: self.vm.$account, type: self.type))
.tabItem {
Image(self.vm.index == 2 ? "profile.selected" : "profile")
}
.tag(2)
}
.toolbar(.hidden, for: .navigationBar)
The only workaround i found is to hide the navigationbar in the TabView and to set new NavigationViews within the child views.
NavigationView {
VStack {
...
}
.navigationTitle(Text("discover"))
.toolbar {
...
}
}
This, however, feels not correct as it is a NavigationView inside a NavigationView and even is buggy (the title and toolbar are sometimes placed below their normal position, kind of like below the hidden, but still exisiting navigation bar of the TabView).
Thus, has anyone found a solution of how to properly combine a NavigationStack with a TabView ?
I want to have a scrollable list that when each row is tapped, it takes you to a different view. Inside each row, I want to have a heart button that when tapped overrides the navigation behavior and just toggles a heart fill/unfill.
As an alternative to do this, would it make sense to use a ScrollView inside a NavigationView and then have each list item be a VStack?
Pseudocode hierarchy:
NavigationView {
ScrollView {
VStack {
// Button
}
}
}
Is there a better way ( or more preferred way) to accomplish this?
Use LazyVStack
let items = ["A","B"]
var body: some View {
ScrollView {
LazyVStack(spacing: 10) {
ForEach(items, id: \.self) { item in
Text(item)
}
}
}
}
I recently started studying ios/swiftui by developing a small application, which has a list of cards to display picture and message loaded from server.
I was using List to hold those cards but the default list 'decoration' (divider, arrow, tapping effect) looks not very good together with my card view. I disable them by adding below code into SceneDelegate:
UITableView.appearance().separatorColor = .clear
UITableViewCell.appearance().selectionStyle = .none
And having some sort of hack to hide the arrow:
List {
ForEach(0..<self.store.data.count, id: \.self) { idx in
NavigationLink(destination: DetailView(item: self.store.data[idx])) {
EmptyView()
}.frame(width: 0).opacity(0)
ItemCard(item: self.store.data[idx])
}
BottomLoader().onAppear {
if self.store.status != .loading {
self.store.load()
}
}
}
But, the problem is that hiding List's selection style and separator color in SceneDelegate applies to all the lists in App, and I do have couple lists (such as the one in settings) need those style.
Then I tried to change the card holder from List to ScrollView but it leads to another trouble, the onAppear callback of last view (BottomLoader) gets called at the same time with ScrollView onAppear gets called.
As far as I understand, onAppear is supposed to be called when view is rendered and shown. List as the item holder, does not have to calculate the full height of all items, that is probably why List renders only the items about to enter screen. But ScrollView does need to know the full height of all items and that is why all the items get rendered.
Below are two small segments to show different behaviors of List and ScrollView:
List {
ForEach(0..<100) { idx in
Text("item # \(idx)").padding().onAppear { print("\(idx) on appear") }
}
}
// only 18 logs printed
And:
ScrollView {
ForEach(0..<100) { idx in
Text("item # \(idx)").padding().onAppear { print("\(idx) on appear") }
}
}
// all 100 logs printed
Is there any way, to let ScrollView acts like List, to render its subviews when they about to enter screen.
Or, is there any way, to disable those default styles to a specific List, not globally?
I am playing around with SwiftUI and I am stuck on this View. everything is working fine but this little bug is very frustrating.I am trying to display the images as a vertical view and it won't show on the view . I know the Images are loaded but the view is not showing it . Its covered in blue color.
import SwiftUI
struct PlanetHome : View {
var planets : [Planet]
var body : some View {
NavigationView {
ScrollView {
ZStack {
Color.black .edgesIgnoringSafeArea (.all)
VStack (alignment: .center)
{
ForEach (self.planets.identified(by: \.imageName))
{
planet in NavigationLink (destination: PlanetDetail(planets: planet))
{
PlanetsView (planets: planet)
.frame (width: 500, height: 500)
.padding (.vertical, 5)
}
}
}
}
}
.navigationBarTitle (Text("Planets"))
}
}
}
I tried to put the NavigationView under the ZStack but it did not work.I have no Idea what I did wrong on the code. No error message on the debugger. just doesn't show the images.
The NavigationLink applies a button style to the objects it holds. Button does the same. To remove the blue color, add the buttonStyle modifier:
NavigationLink(destination: ...) {
...
}
.buttonStyle(PlainButtonStyle())
You can create and apply your own button style as well.
Its covered in blue color.
You can change blue color to whatever you want with .foregroundColor(.YourColor) or just change Render as Default to Render as Original Image at Assets.xcassets -> Pick Any Image -> Show the Attribute inspector
To fix other problem you should include PlanetsView because when i put Image(systemName: "photo") instead your view it's shows correctly