I have created a TabBar which is a view at the root of the app.
I'd like to hide the TabBar component when navigating from the closet view to a subview.
The Closet view is a NavigationView containing multiple NavigationLink
Here is the root view of the app:
struct Home: View {
#State var selected = 0
#ObservedObject var viewModel: HomeViewModel
init(viewModel: HomeViewModel) {
self.viewModel = viewModel
}
var body: some View {
ZStack {
if self.selected == 0 {
// viewModel.feedView
Stylist()
}
else if self.selected == 1 {
OutfitView()
}
else if self.selected == 2 {
viewModel.closetView
} else {
Calendar()
}
VStack {
Spacer()
TabBar(selected: self.$selected)
.frame(width: UIScreen.main.bounds.width - 20, alignment: .center)
}
}
}
Here is the TabBar component:
var body : some View{
HStack{
Spacer(minLength: 0)
HStack{
Button(action: {
self.selected = 0
}) {
Image(systemName: "bolt.fill").foregroundColor(self.selected == 0 ? Color("GradientMiddle") : .gray).padding(.horizontal).font(.system(size: 20))
}
Spacer(minLength: 15)
Button(action: {
self.selected = 1
}) {
Image(systemName: "sun.min.fill").foregroundColor(self.selected == 1 ? Color("GradientMiddle") : .gray).padding(.horizontal).font(.system(size: 20))
}
Spacer(minLength: 15)
Button(action: {
self.selected = 2
}) {
Image(systemName: "cube.box.fill").foregroundColor(self.selected == 2 ? Color("GradientMiddle") : .gray).padding(.horizontal).font(.system(size: 20))
}
Spacer(minLength: 15)
Button(action: {
self.selected = 4
}) {
Image(systemName: "calendar").foregroundColor(self.selected == 4 ? Color("GradientMiddle") : .gray).padding(.horizontal).font(.system(size: 20))
}
}.padding(.vertical, 20)
.padding(.horizontal)
.background(Color(UIColor.systemGray5))
.clipShape(Capsule())
.padding(42)
.animation(.interactiveSpring(response: 0.6, dampingFraction: 0.6, blendDuration: 0.6))
}
}
Is there an way to that properly ?
Update:
When I try #Asperi solution I have an error:
Value of type 'some View' has no member 'observingNavigate'
Maybe that could be caused by how I create closetView ?
So here is how I create ClosetView:
HomeViewModel.swift:
class HomeViewModel: ObservableObject {
}
extension HomeViewModel {
var closetView: some View {
return HomeBuilder.makeClosetView()
}
}
HomeBuilder.swift:
enum HomeBuilder {
static func makeClosetView() -> some View {
let viewModel = ClosetViewModel()
return Closet(viewModel: viewModel)
}
}
You need a callback from your Closet view so root view can perform some action. Here is a demo of possible solution.
Let's assume we have the following demo ClosetView
struct ClosetView: View {
var didNavigate: ((Bool) -> Void)?
var body: some View {
NavigationView {
NavigationLink("Link", destination:
Text("Demo")
.onAppear { didNavigate?(true) }
.onDisappear { didNavigate?(false) }
)
}
}
func observingNavigate(callback: #escaping ((Bool) -> Void)) -> some View {
var view = self
view.didNavigate = callback
return view
}
}
then changes in Home will be the following
#State private var showTabbar = true // << state !!
var body: some View {
ZStack {
if self.selected == 0 {
// viewModel.feedView
Stylist()
}
else if self.selected == 1 {
OutfitView()
}
else if self.selected == 2 {
viewModel.closetView
.observingNavigate { self.showTabbar = !$0 } // << here !!
} else {
Calendar()
}
VStack {
Spacer()
if showTabbar { // << here !!
TabBar(selected: self.$selected)
.frame(width: UIScreen.main.bounds.width - 20, alignment: .center)
}
}
}
}
Note: I don't see how you create viewModel.closetView so callback is injected as property, however it can also be injected via constructor arguments, but this does not change idea much.
Related
I have a custom bounce animation applied to a view with multiple inner views which can change depending on conditions. My issue is that when the inner view changes, the animation applied to a parent no longer applies to it.
Example:
Sample Code:
struct ContentView: View {
#State var number = 1
var body: some View {
VStack {
ZStack {
Circle()
.foregroundColor(.gray)
.frame(width: 100, height: 100, alignment: .center)
if number == 1 {
Image(systemName: "person")
}
else if number == 2 {
ProgressView()
}
else {
EmptyView()
}
}
.bounceEffect()
Button {
if number != 3 {
number += 1
}
else {
number = 1
}
} label: {
Text("CHANGE")
}
.padding()
}
.padding()
}
}
struct BounceEffect: ViewModifier {
#State var bounce = false
var allow: Bool
func body(content: Content) -> some View {
content
.offset(y: (bounce && allow) ? -5 : 0)
.animation(.interpolatingSpring(mass: 1, stiffness: 350, damping: 5, initialVelocity: 10).repeatForever(autoreverses: false).delay(1), value: UUID())
.onAppear {
if allow {
bounce.toggle()
}
}
}
}
extension View {
func bounceEffect(allow: Bool = true) -> some View {
modifier(BounceEffect(allow: allow))
}
}
Thank you.
The reason of the bounceEffect don't get the button after you change because you are creating the new image when ever you tap the button change.
The action you need here: just only change the system image of the image not creating new one every time the button change is tapped
The code will be like this
struct ContentView: View {
#State var number = 1
#State var imageName = "person"
var body: some View {
VStack {
ZStack {
Circle()
.foregroundColor(.gray)
.frame(width: 100, height: 100, alignment: .center)
Image(systemName: imageName)
}
.bounceEffect()
Button {
if number != 3 {
number += 1
}
else {
number = 1
}
if number == 1 {
imageName = "person"
}
else if number == 2 {
imageName = "globe"
}
else {
imageName = "square"
}
} label: {
Text("CHANGE")
}
.padding()
}
.padding()
}
}
The result
When the new images appear, they aren’t the ones involved in the animation. One way to fix this is to include all 3 images from the start, and just cycle their visibility by changing opacity:
ZStack {
Circle()
.foregroundColor(.gray)
.frame(width: 100, height: 100, alignment: .center)
Image(systemName: "person")
.opacity(number == 1 ? 1 : 0)
Image(systemName: "globe")
.opacity(number == 2 ? 1 : 0)
Image(systemName: "square")
.opacity(number == 3 ? 1 : 0)
}
#bewithyou’s answer is more scalable and should be the accepted answer. Here is a better way to structure the selection of your images:
struct ContentView: View {
#State var number = 0
let names = ["person", "globe", "square", "house", "car"]
var body: some View {
VStack {
ZStack {
Circle()
.foregroundColor(.gray)
.frame(width: 100, height: 100, alignment: .center)
Image(systemName: names[number])
}
.bounceEffect()
Button {
number = (number + 1) % names.count
} label: {
Text("CHANGE")
}
.padding()
}
.padding()
}
}
I have sub-navigation inside Listview and trying to achieve leftSlide and rightSlide animation but it always shows default slide animation. I even tried to add .transition(.move(edge: . leading)) & .transition(.move(edge: . trailing))
import SwiftUI
import Combine
struct GameTabView: View {
#State var selectedTab: Int = 0
init() {
UITableView.appearance().sectionHeaderTopPadding = 0
}
var body: some View {
listView
.ignoresSafeArea()
}
var listView: some View {
List {
Group {
Color.gray.frame(height: 400)
sectionView
}
.listRowInsets(EdgeInsets())
}
.listStyle(.plain)
}
var sectionView: some View {
Section {
tabContentView
.transition(.move(edge: . leading)) // NOT WORKING
.background(Color.blue)
} header: {
headerView
}
}
private var headerView: some View {
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 16) {
Button {
withAnimation {
selectedTab = 0
}
} label: {
Text("AAAA")
.padding()
}
Button {
withAnimation {
selectedTab = 1
}
} label: {
Text("BBBB")
.padding()
}
Button {
withAnimation {
selectedTab = 2
}
} label: {
Text("BBBB")
.padding()
}
}
}
}
.background(Color.green)
}
#ViewBuilder private var tabContentView: some View {
switch selectedTab {
case 0:
DummyScreen(title: "FIRST", color: .red)
case 1:
DummyScreen(title: "SECOND", color: .green)
case 2:
DummyScreen(title: "THIRD", color: .blue)
default:
EmptyView()
}
}
}
struct DummyScreen: View {
let title: String
let color: Color
var body: some View {
VStack {
ForEach(0..<15, id: \.self) { index in
HStack {
Text("#\(index): title \(title)")
.foregroundColor(Color.black)
.font(.system(size: 30))
.padding(.vertical, 20)
Spacer()
}
.background(Color.yellow)
}
}
.background(color)
}
}
Just like Asperi said, you can change to another type of View to make the transition works.
I tried your code, changed List to VStack with ScrollView inside to wrap around the tabContentView, and the result of the UI showed the same except with a proper animation now, and you don't have to manually adjust the height of your contents since HStack height is dynamic based on your Text() growth.
Edited: header fixed, animation fixed
import SwiftUI
import SwiftUITrackableScrollView //Added
import Combine
struct GameTabView: View {
#State private var scrollViewContentOffset = CGFloat(0) //Added
#State var selectedTab: Int = 0
init() {
UITableView.appearance().sectionHeaderTopPadding = 0
}
var body: some View {
listView
.ignoresSafeArea()
}
var listView: some View {
ZStack { //Added
TrackableScrollView(.vertical, showIndicators: true, contentOffset: $scrollViewContentOffset) {
VStack {
Color.gray.frame(height: 400)
sectionView
}
}
if(scrollViewContentOffset > 400) {
VStack {
headerView
Spacer()
}
}
}
}
var sectionView: some View {
Section {
tabContentView
.transition(.scale) // FIXED
.background(Color.blue)
} header: {
headerView
}
}
private var headerView: some View {
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 16) {
Button {
withAnimation {
selectedTab = 0
}
} label: {
Text("AAAA")
.padding()
}
Button {
withAnimation {
selectedTab = 1
}
} label: {
Text("BBBB")
.padding()
}
Button {
withAnimation {
selectedTab = 2
}
} label: {
Text("BBBB")
.padding()
}
}
}
}
.background(Color.green)
}
#ViewBuilder private var tabContentView: some View {
switch selectedTab {
case 0:
DummyScreen(title: "FIRST", color: .red)
case 1:
DummyScreen(title: "SECOND", color: .green)
case 2:
DummyScreen(title: "THIRD", color: .blue)
default:
EmptyView()
}
}
}
struct DummyScreen: View {
let title: String
let color: Color
var body: some View {
VStack {
ForEach(0..<15, id: \.self) { index in
HStack {
Text("#\(index): title \(title)")
.foregroundColor(Color.black)
.font(.system(size: 30))
.padding(.vertical, 20)
Spacer()
}
.background(Color.yellow)
}
}
.background(color)
}
}
Thanks to #tail and #Asperi
I finally got the solution via updating ScrollView with ScrollviewProxy:
import SwiftUI
import Combine
struct GameTabView: View {
#State var selectedTab: Int = 0
#State var proxy: ScrollViewProxy?
init() {
UITableView.appearance().sectionHeaderTopPadding = 0
}
var body: some View {
listView
.ignoresSafeArea()
}
var listView: some View {
List {
Group {
Color.gray.frame(height: 400)
sectionView
}
.listRowInsets(EdgeInsets())
}
.listStyle(.plain)
}
var sectionView: some View {
Section {
tabContentView
} header: {
headerView
}
}
private var headerView: some View {
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 16) {
Button {
withAnimation {
selectedTab = 0
self.proxy?.scrollTo(0, anchor: .center)
}
} label: {
Text("AAAA")
.padding()
}
Button {
withAnimation {
selectedTab = 1
self.proxy?.scrollTo(1, anchor: .center)
}
} label: {
Text("BBBB")
.padding()
}
Button {
withAnimation {
selectedTab = 2
self.proxy?.scrollTo(2, anchor: .center)
}
} label: {
Text("BBBB")
.padding()
}
}
}
}
.background(Color.green)
}
#ViewBuilder private var tabContentView: some View {
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 0) {
ForEach(0..<3, id: \.self) { i in
DummyScreen(title: "Name: \(i)", color: .blue)
.frame(idealWidth: UIScreen.main.bounds.width)
.id(i)
}
}
}
.onAppear {
self.proxy = proxy
}
}
}
}
firstly I am really new to iOS development and Swift (2 weeks coming here from PHP :))
I am trying to build a iOS application that has a side menu. And my intention is when I click on a item in the menu the new view will appear on screen like the 'HomeViewController' and for each consequent item like example1, 2 etc (In place of the menu button, Note I will be adding the top nav bar soon to open the menu)
I am wondering how I can accomplish this feature?
Thanks
ContentView.swift
import SwiftUI
struct MenuItem: Identifiable {
var id = UUID()
let text: String
}
func controllView(clickedview:String) {
print(clickedview)
}
struct MenuContent: View{
let items: [MenuItem] = [
MenuItem(text: "Home"),
MenuItem(text: "Example1"),
MenuItem(text: "Example2"),
MenuItem(text: "Example3")
]
var body: some View {
ZStack {
Color(UIColor(red: 33/255.0, green: 33/255.0, blue: 33/255.0, alpha: 1))
VStack(alignment: .leading, spacing: 0) {
ForEach(items) {items in
HStack {
Text(items.text)
.bold()
.font(.system(size: 20))
.multilineTextAlignment(/*#START_MENU_TOKEN#*/.leading/*#END_MENU_TOKEN#*/)
.foregroundColor(Color.white)
Spacer()
}
.onTapGesture {
controllView(clickedview: items.text)
}
.padding()
Divider()
}
Spacer()
}
.padding(.top, 40)
}
}
}
struct SideMenu: View {
let width: CGFloat
let menuOpen: Bool
let toggleMenu: () -> Void
var body: some View {
ZStack {
//Dimmed backgroud
GeometryReader { _ in
EmptyView()
}
.background(Color.gray.opacity(0.15))
.opacity(self.menuOpen ? 1 : 0)
.animation(Animation.easeIn.delay(0.25))
.onTapGesture {
self.toggleMenu()
}
//Menucontent
HStack {
MenuContent()
.frame(width: width)
.offset(x: menuOpen ? 0 : -width)
.animation(.default)
Spacer()
}
}
}
}
struct ContentView: View {
#State var menuOpen = false
var body: some View {
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
if menuOpen {
withAnimation {
print("Left")
menuOpen.toggle()
}
}
}
if $0.translation.width > -100 {
if !menuOpen {
withAnimation {
print("Right")
menuOpen.toggle()
}
}
}
}
ZStack {
if !menuOpen {
Button(action: {
self.menuOpen.toggle()
}, label: {
Text("Open Menu")
.bold()
.foregroundColor(Color.white)
.frame(width: 200, height: 50, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.background(Color(.systemBlue))
})
}
SideMenu(width: UIScreen.main.bounds.width/1.6, menuOpen: menuOpen, toggleMenu: toggleMenu)
}
.edgesIgnoringSafeArea(.all)
.gesture(drag)
}
func toggleMenu(){
menuOpen.toggle()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The on tap command from the above code:
.onTapGesture {
controllView(clickedview: items.text)
}
HomeViewController.swift
import UIKit
import WebKit
class HomeViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://developer.apple.com")!
webView.load(URLRequest(url: url))
}
}
You'll need a couple of ingredients:
A way to store the state of the currently active view
A way to communicate the state between your menu and main content view
For the first one, I made an enum that listed the different types of views (ViewType) and added it to your MenuItem model.
For the second, you can pass state via a #Binding from parent to child views and back up the chain.
struct MenuItem: Identifiable {
var id = UUID()
let text: String
let viewType : ViewType
}
enum ViewType {
case home, example1, example2, example3
}
struct MenuContent: View{
#Binding var activeView : ViewType
let items: [MenuItem] = [
MenuItem(text: "Home", viewType: .home),
MenuItem(text: "Example1", viewType: .example1),
MenuItem(text: "Example2", viewType: .example2),
MenuItem(text: "Example3", viewType: .example3)
]
var body: some View {
ZStack {
Color(UIColor(red: 33/255.0, green: 33/255.0, blue: 33/255.0, alpha: 1))
VStack(alignment: .leading, spacing: 0) {
ForEach(items) { item in
HStack {
Text(item.text)
.bold()
.font(.system(size: 20))
.multilineTextAlignment(.leading)
.foregroundColor(Color.white)
Spacer()
}
.onTapGesture {
activeView = item.viewType
}
.padding()
Divider()
}
Spacer()
}
.padding(.top, 40)
}
}
}
struct SideMenu: View {
let width: CGFloat
let menuOpen: Bool
let toggleMenu: () -> Void
#Binding var activeView : ViewType
var body: some View {
ZStack {
//Dimmed backgroud
GeometryReader { _ in
EmptyView()
}
.background(Color.gray.opacity(0.15))
.opacity(self.menuOpen ? 1 : 0)
.animation(Animation.easeIn.delay(0.25))
.onTapGesture {
self.toggleMenu()
}
//Menucontent
HStack {
MenuContent(activeView: $activeView)
.frame(width: width)
.offset(x: menuOpen ? 0 : -width)
.animation(.default)
Spacer()
}
}
}
}
struct ContentView: View {
#State private var menuOpen = false
#State private var activeView : ViewType = .home
var body: some View {
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
if menuOpen {
withAnimation {
print("Left")
menuOpen.toggle()
}
}
}
if $0.translation.width > -100 {
if !menuOpen {
withAnimation {
print("Right")
menuOpen.toggle()
}
}
}
}
ZStack {
VStack {
if !menuOpen {
Button(action: {
self.menuOpen.toggle()
}, label: {
Text("Open Menu")
.bold()
.foregroundColor(Color.white)
.frame(width: 200, height: 50, alignment: .center)
.background(Color(.systemBlue))
})
}
switch activeView {
case .home:
HomeViewControllerRepresented()
case .example1:
Text("Example1")
case .example2:
Text("Example2")
case .example3:
Text("Example3")
}
}
SideMenu(width: UIScreen.main.bounds.width/1.6,
menuOpen: menuOpen,
toggleMenu: toggleMenu,
activeView: $activeView)
.edgesIgnoringSafeArea(.all)
}
.gesture(drag)
}
func toggleMenu(){
menuOpen.toggle()
}
}
struct HomeViewControllerRepresented : UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> HomeViewController {
HomeViewController()
}
func updateUIViewController(_ uiViewController: HomeViewController, context: Context) {
}
}
I have tried to simulate how displaying the call view on the top of all. But I have some unclear points:
Is there any way to make it more simple?
Will the view hierarchy protected for that cases?
AppView.swift
import SwiftUI
struct AppView: View {
#ObservedObject var callVM: CallViewModel
init() {
self.callVM = CallViewModel()
}
var body: some View {
VStack {
IncomingCallView(rootView: appView, isActive: self.$callVM.isIncomingCallActive)
TabView {
TabOne()
.tabItem {
Image(systemName: "list.dash")
Text("Menu")
}
TabTwo()
.tabItem {
Image(systemName: "square.and.pencil")
Text("Order")
}
}
}
.onAppear(perform: load)
}
var appView: some View {
Text("")
}
func load() {
self.callVM.getCall()
}
}
struct AppView_Previews: PreviewProvider {
static var previews: some View {
AppView()
}
}
CallViewModel.swift
import Foundation
class CallViewModel: ObservableObject {
#Published public var isIncomingCallActive = Bool()
init() {
self.isIncomingCallActive = false
}
func getCall() {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.isIncomingCallActive = true
}
}
}
IncomingCallView.swift
import SwiftUI
struct IncomingCallView<RootView: View>: View {
private let rootView: RootView
#Binding var isActive: Bool
init(rootView: RootView, isActive: Binding<Bool>) {
self.rootView = rootView
self._isActive = isActive
}
var body: some View {
rootView
.background(Activator(isActive: $isActive))
}
struct Activator: UIViewRepresentable {
#Binding var isActive: Bool
#State private var myWindow: UIWindow? = nil
func makeUIView(context: Context) -> UIView {
let view = UIView()
DispatchQueue.main.async {
self.myWindow = view.window
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
guard let holder = myWindow?.rootViewController?.view else { return }
if isActive && context.coordinator.controller == nil {
context.coordinator.controller = UIHostingController(rootView: loadingView)
let view = context.coordinator.controller!.view
view?.backgroundColor = UIColor.black.withAlphaComponent(1)
view?.isUserInteractionEnabled = true
view?.translatesAutoresizingMaskIntoConstraints = false
holder.addSubview(view!)
holder.isUserInteractionEnabled = true
view?.leadingAnchor.constraint(equalTo: holder.leadingAnchor).isActive = true
view?.trailingAnchor.constraint(equalTo: holder.trailingAnchor).isActive = true
view?.topAnchor.constraint(equalTo: holder.topAnchor).isActive = true
view?.bottomAnchor.constraint(equalTo: holder.bottomAnchor).isActive = true
} else if !isActive {
context.coordinator.controller?.view.removeFromSuperview()
context.coordinator.controller = nil
holder.isUserInteractionEnabled = true
}
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator {
var controller: UIViewController? = nil
}
private var loadingView: some View {
HStack(spacing: 20) {
Button(action: {
print("acceptCall pressed")
// change UI for accepted call
}) {
Image("acceptCall")
.resizable()
.frame(width: 50, height: 50, alignment: .center)
.aspectRatio(contentMode: .fit)
}
Button(action: {
// close the view
print("rejectCall pressed")
self.isActive = false
}) {
Image("rejectCall")
.resizable()
.frame(width: 50, height: 50, alignment: .center)
.aspectRatio(contentMode: .fit)
}
}
.frame(width: 300, height: 300)
.background(Color.primary.opacity(0.7))
.cornerRadius(10)
}
}
}
Something in just swiftUI would look like this
struct AppView: View {
#StateObject var callVM = CallViewModel()
var body: some View {
ZStack {
TabView {
Text("TabOne")
.tabItem {
Image(systemName: "list.dash")
Text("Menu")
}
Text("TabTwo")
.tabItem {
Image(systemName: "square.and.pencil")
Text("Order")
}
}
if callVM.isIncomingCallActive {
ZStack {
Color.green
VStack {
Text("Incoming call...")
Button(action: { callVM.isIncomingCallActive = false }) {
Text("Cancel")
}
}
}.edgesIgnoringSafeArea(.all) // <= here
}
}
.onAppear {
self.callVM.getCall()
}
}
}
class CallViewModel: ObservableObject {
#Published public var isIncomingCallActive = false
func getCall() {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.isIncomingCallActive = true
}
}
}
I am trying to create pagination in SwiftUI with a List but it's not behaving correctly when scrolled and it's size changes.
Here is the code which I am using for it.
class ViewModel: ObservableObject {
#Published var feeds:[String] = []
init() {
for i in 0..<10{
feeds.append("Page \(i)")
}
}
func appentNewData() -> Void{
for i in self.feeds.count..<(self.feeds.count + 10){
feeds.append("New Page \(i)")
}
}
}
struct ContentView: View {
#ObservedObject var viewModel = ViewModel()
#State var isExpanded:Bool = false
var body: some View {
VStack(spacing:0){
Button(action: {
withAnimation {
self.isExpanded.toggle()
}
}) {
Text("Search Bar ( Tap to expand )")
.font(.title)
.foregroundColor(Color.black)
}
.frame(height: self.isExpanded ? 200 : 100)
.frame(maxWidth:.infinity)
.background(Color.red)
GeometryReader { (proxy) in
List{
ForEach(self.viewModel.feeds, id:\.self) { feed in
FeedView(text: feed, size: proxy.size)
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
.frame(width:proxy.size.width, height: proxy.size.height)
.border(Color.green ,width: 5.0)
.onAppear {
guard let lastItem = self.viewModel.feeds.last else{
return
}
if lastItem == feed{
self.viewModel.appentNewData()
}
}
}
}
.frame(width:proxy.size.width, height: proxy.size.height)
.background(Color.purple)
.onAppear {
UITableViewCell.appearance().selectionStyle = .none
UITableView.appearance().separatorStyle = .none
UITableView.appearance().isPagingEnabled = true
UITableView.appearance().separatorStyle = .none
UITableView.appearance().showsVerticalScrollIndicator = false
}.onDisappear {
UITableView.appearance().isPagingEnabled = false
UITableView.appearance().showsVerticalScrollIndicator = true
}
}
}.clipped()
}
}
struct FeedView:View {
#State var text:String
var size:CGSize
var body: some View{
VStack(spacing: 20.0){
Text(text)
.font(.largeTitle)
HStack{
Text("Width : \(size.width)")
.font(.headline)
Text("Height : \(size.height)")
.font(.headline)
}
}
.frame(maxWidth:.infinity,maxHeight: .infinity,alignment: .center)
}
}
It behaves like this.
Not sure why it is not dividing the row equally since each row has the same width and height.
Here is the quick project if anyone would like to try out.