4

I'm using a tab view using the UIPageViewController behaviour. So I defined the following model:

class WalktroughModel: Identifiable, ObservableObject {
  let id: UUID = UUID()
  let imageName: String
  let title: String

  init(imageName: String, title: String) {
      self.imageName = imageName
      self.title = title
  }
}

Now I use this swiftUI view as a child view of tab view:

struct WalktroughAsset: View {

  @StateObject var asset: WalktroughModel

  var body: some View {
      Image(asset.imageName)
          .resizable()
          .overlay(Color.black.opacity(0.43))
          .overlay(
              VStack{
                  Spacer()
                  Text(asset.title)
                      .foregroundColor(.white)
                      .font(.custom("OpenSans-regular", size: 22.0))
              }
              .padding(.bottom, 64)
          )
    }
}

In my content view I have the following:

struct ContentView: View {

   var thumbs: [WalktroughModel] = [WalktroughModel(imageName: "splash-1", title: "Concepto 1"), WalktroughModel(imageName: "splash-2", title: "Concepto 2"), WalktroughModel(imageName: "splash-3", title: "Concepto 3")]

   var body: some View {
       ZStack {
           Color.black.overlay(TabView{
               ForEach(thumbs) {
                   image in
                   WalktroughAsset(asset: image)
               }
           }
           .tabViewStyle(PageTabViewStyle())
           .padding([.bottom, .top], 32)
           )
        
       }
       .edgesIgnoringSafeArea(/*@START_MENU_TOKEN@*/.all/*@END_MENU_TOKEN@*/)
    
   }
}

Now, when I build and run the memory jumps 80 mb to 160 mb when I swipe to the other view and jumps to 230 mb when I swipe to the third view. What could be happen?

Best Regards

Alfredo Luco G
  • 876
  • 7
  • 18
  • Most likely it's loading all the views multiple times. Could try some thing like this https://stackoverflow.com/a/61234030/4080925 even tho it's not a navigation it could solve you're issue. – MwcsMac Jul 12 '20 at 04:40
  • @MwcsMac but how can i avoid that in tabview? – Alfredo Luco G Jul 12 '20 at 05:31
  • Probably you have very big images. – Asperi Jul 12 '20 at 05:55
  • @Asperi that's right, but I want release that memory. How can i get it? – Alfredo Luco G Jul 12 '20 at 06:05
  • @AlfredoLucoG Did you ever solve this? I have an almost identical problem that I am trying to solve at the moment. I would have thought SwiftUI would release the memory the sub views are using once they were out of sight, but it doesn't seem to be the case. – James Woodcock Aug 27 '20 at 13:39
  • Hi @JamesWoodcock unfortunatelly I couldn't solve this. Maybe it seems a SwiftUI's bug. – Alfredo Luco G Aug 27 '20 at 18:28
  • 1
    Does Instruments/Leaks shows memory leak? If yes - would you show the leaked stack/objects? If not - then there is no leaks - we work with ARC, so run-time decides by itself when to remove released resources from memory. – Asperi Aug 29 '20 at 08:56
  • Hi, I have the exact same issue. I don't know if you have still this piece of code. But it's only this part : .tabViewStyle(PageTabViewStyle()), if I removed it, no memory issues.. So the swiping effect is the problem. Don't know how to fix it though.. – Maurice Feb 11 '21 at 10:06
  • I'm seeing this with TabView on iOS 15. Memory use is slowly growing, even without swiping across pages. I can freeze an iPhone SE after just a few hours. It happens even with a very basic TabView with PageTabViewStyle (only a simple static Text element in each page). – Alex Jan 08 '22 at 20:59
  • Adding to my comment that my view is observing @Published variables so I guess that is creating the same memory issue than actually sliding the pages. – Alex Jan 09 '22 at 11:45

2 Answers2

3

This code fixes a memory leak for me

struct TabViewWrapper<Content: View, Selection: Hashable>: View {
    @Binding var selection: Selection
    @ViewBuilder let content: () -> Content
    
    var body: some View {
        TabView(selection: $selection, content: content)
    }
}

Replace TabView(selection:) to TabViewWrapper(selection:)

TabViewWrapper(selection: $selection) {
    tabContent
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))

This bug is presented when you use custom view with args

stalkermv
  • 61
  • 4
0

Similar issue on iOS 15 (Tested on 15.2)

Memory use for the below SwiftUI app will continuously increase over time. The app is simply showing a TabView in a full screen cover, and has a timer that refreshes a countdown every second. If I display a simple text element instead of the TabView, the memory use will behave properly. I submitted a feedback to Apple.

import SwiftUI

class ContentView_manager : ObservableObject {
    var view_timer: Timer?
    @Published var countdown_txt: String?
    
    func startTimer(){
        if self.view_timer == nil  {
                       self.view_timer = Timer(fireAt: Date(), interval: 1, target: self, selector: #selector(self.timer_action), userInfo: nil, repeats: true)
                       RunLoop.current.add(self.view_timer!, forMode: RunLoop.Mode.common)
        }
    }
    
    func stopTimer(){
        if self.view_timer != nil  {
            self.view_timer?.invalidate()
            self.view_timer = nil
        }
    }
    
    @objc
    func timer_action(){
        //update number of seconds until January 2023
        print("Timer Action")
        self.countdown_txt = String(DateInterval(start: Date(), end: Date(timeIntervalSince1970: 1672527600)).duration)
    }
}

@main
struct debugApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(view_manager:ContentView_manager())
        }
    }
}

struct ContentView : View {
    @ObservedObject var view_manager : ContentView_manager
    @State var show_cover : Bool = false
    var body: some View {
       
        VStack{
            Button(action: {
                self.show_cover = true
            }, label: {Text("Show screen cover with TabView")})
        }
        .fullScreenCover(isPresented: self.$show_cover){ScreenCover(view_manager: view_manager)}
    }
}

struct ScreenCover: View {
    @ObservedObject var view_manager : ContentView_manager
    var body: some View {
        tab(txt:view_manager.countdown_txt)
        .onAppear(perform: {
            view_manager.startTimer()
        })
        .onDisappear(perform: {
            view_manager.stopTimer()
        })
        
    }
}


struct tab: View {
    var txt: String?
    var body: some View {
        TabView{
            Text("First Tab")
                   .tabItem {
                       Image(systemName: "1.square.fill")
                       Text("First")
                   }
            Text("Next year in \(txt ?? "") seconds!")
                   .tabItem {
                       Image(systemName: "2.square.fill")
                       Text("Second")
                   }
               Text("Third tab")
                   .tabItem {
                       Image(systemName: "3.square.fill")
                       Text("Third")
                   }
        }
        .tabViewStyle(PageTabViewStyle())
        
    }
}
Alex
  • 91
  • 6