在SwiftUI中使用图像和PageView时内存耗尽。

问题描述 投票:0回答:1

我正在写一个应用程序,可以向用户展示大图片,他们可以翻阅和放大,类似于照片应用程序或一本书。我正在尝试用SwiftUI来编写这个应用。我一直在遵循 苹果网站上的SwiftUI教程 我已经修改了代码,以读取一个json文件来创建一个 "Imageslides "数组--一种保存图像和有关它们的信息的方式。然后我使用PageView和PageViewController来显示ContentView>ContentView显示图片和一些按钮,这些按钮显示了关于我正在显示的图片的文本。我的问题是,当我在页面中滑动时,内存会上升,直到应用程序崩溃。现在,如果我使用的是UIKit,我可以使用

if let imgPath = Bundle.main.path(forResource: name, ofType: nil)
    {
        return UIImage(contentsOfFile: imgPath)
    }

但我看不到如何在 swiftUI 中使用 Image 来使用 contentsOfFile,或者用其他方式让图片在不需要的时候脱离内存。我把图片放在资源文件夹里,而不是资产库里。谢谢您的帮助。

//data.swift - reading from json, mainly from apple's tutorials
import Foundation
import UIKit
import SwiftUI

let imageData: [Imageslide] = load("imageData.json")

func load<T: Decodable>(_ filename: String) -> T {
    let data: Data

    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
        else {
            fatalError("Couldn't find \(filename) in main bundle.")
    }

    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }

    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

final class ImageStore {
    typealias _ImageDictionary = [String: CGImage]
    fileprivate var images: _ImageDictionary = [:]

    fileprivate static var scale = 2

    static var shared = ImageStore()

    func image(name: String) -> Image {
        let index = _guaranteeImage(name: name)

        return Image(images.values[index], scale: CGFloat(ImageStore.scale), label: Text(name))
    }

    static func loadImage(name: String) -> CGImage {
        guard
            let url = Bundle.main.url(forResource: name, withExtension: "png"),
            let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil),
            let image = CGImageSourceCreateImageAtIndex(imageSource, 0, nil)
        else {
            fatalError("Couldn't load image \(name).jpg from main bundle.")
        }
        return image
    }

    fileprivate func _guaranteeImage(name: String) -> _ImageDictionary.Index {
        if let index = images.index(forKey: name) { return index }

        images[name] = ImageStore.loadImage(name: name)
        return images.index(forKey: name)!
    }
}

//imageslide.swift - abstract for holding my image slide and button info 

import SwiftUI

struct buttonInfo: Hashable, Codable{
    var text: String? = nil
    var coords: [Int]? = nil
    public enum CodingKeys : String, CodingKey {
        case text, coords
    }
    init(from decoder: Decoder) throws {
      let container = try decoder.container(keyedBy: CodingKeys.self)
      self.text = try container.decodeIfPresent(String.self, forKey: .text)
      self.coords = try container.decodeIfPresent([Int].self, forKey: .coords)
    }
      func encode(to encoder: Encoder) throws {
      var container = encoder.container(keyedBy: CodingKeys.self)
      try container.encode(self.text, forKey: .text)
      try container.encode(self.coords, forKey: .coords)
    }
}

struct Imageslide: Hashable, Codable, Identifiable {

    public var imagename: String
   public var id: Int
    public var noButtons: Int

    public var buttoninfo: [buttonInfo]? = nil

    public enum CodingKeys : String, CodingKey {
        case imagename, id, noButtons, buttoninfo
        case imageslide = "Imageslide"
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let imageslide = try container.nestedContainer(keyedBy:
        CodingKeys.self, forKey: .imageslide)
        self.imagename = try imageslide.decode(String.self, forKey: .imagename)
        self.id = try imageslide.decode(Int.self, forKey: .id)
        self.noButtons = try imageslide.decode(Int.self, forKey: .id)
        self.buttoninfo = try imageslide.decodeIfPresent([buttonInfo].self, forKey: .buttoninfo)
    }

   func encode(to encoder: Encoder) throws {
     var container = encoder.container(keyedBy: CodingKeys.self)
     var imageslide = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .imageslide)
     try imageslide.encode(self.imagename, forKey: .imagename)
     try imageslide.encode(self.id, forKey: .id)
     try imageslide.encode(self.noButtons, forKey: .noButtons)
    try imageslide.encode(self.buttoninfo, forKey: .buttoninfo)
    }
}

extension Imageslide {
    var image: Image {
        ImageStore.shared.image(name: imagename)
    }
}



struct ButtonDataStore: Codable{
    var buttonData: [buttonInfo]
}

//contentview.swift - used to show the images and buttons
import SwiftUI

struct ContentView: View {
    var imageslide: Imageslide
    @State private var button1Visible = true
    @State var scale: CGFloat = 1.0

    var body: some View {

        ZStack {


            Image(uiImage: (UIImage(named: imageslide.imagename)!))
           //   imageslide.image
                //   Image(imageslide.imagename)
                .resizable()
           .frame(width: 2732/2, height: 2048/2)
      //Pinch to zoom code goes here

                .background(/*@START_MENU_TOKEN@*/Color.black/*@END_MENU_TOKEN@*/)

                    .edgesIgnoringSafeArea(.all)
                .statusBar(hidden: true)

            if (imageslide.noButtons != 0)
            {
                if imageslide.buttoninfo != nil
                {
                    ForEach(imageslide.buttoninfo!, id: \.self) {count in

                        ButtonView(text: count.text!, xcoord: count.coords![0], ycoord: count.coords![1], xcoordo: count.coords![2], ycoordo: count.coords![3])
                    }
                }
        }
        }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {

        //ContentView(imageslide: imageData[0])
        ContentView(imageslide: imageData[1]).previewLayout(
            .fixed(width: 2732/2, height: 2048/2)     
        )
    }
}
}


//PageViewController.swift

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]
    @Binding var currentPage: Int

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
         let pageViewController = UIPageViewController(
             transitionStyle: .scroll,
             navigationOrientation: .horizontal)
        pageViewController.dataSource = context.coordinator
        pageViewController.delegate = context.coordinator

         return pageViewController
     }


    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
             [controllers[currentPage]], direction: .forward, animated: true)
    }


    class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }


    func pageViewController(
        _ pageViewController: UIPageViewController,
        viewControllerBefore viewController: UIViewController) -> UIViewController?
    {
        guard let index = parent.controllers.firstIndex(of: viewController) else {
            return nil
        }
        if index == 0 {
            return parent.controllers.last
        }
        return parent.controllers[index - 1]
    }

    func pageViewController(
        _ pageViewController: UIPageViewController,
        viewControllerAfter viewController: UIViewController) -> UIViewController?
    {
        guard let index = parent.controllers.firstIndex(of: viewController) else {
            return nil
        }
        if index + 1 == parent.controllers.count {
            return parent.controllers.first
        }
        return parent.controllers[index + 1]

    }

    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        if completed,
            let visibleViewController = pageViewController.viewControllers?.first,
            let index = parent.controllers.firstIndex(of: visibleViewController)
        {
            parent.currentPage = index
        }
    }
}

}


//pageview.swift 
import SwiftUI

struct PageView<Page: View>: View {
    var viewControllers: [UIHostingController<Page>]

    @State var currentPage = 0

    init(_ views: [Page]) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
    }

    var body: some View {
         PageViewController(controllers: viewControllers, currentPage: $currentPage)
    }
}

struct PageView_Previews: PreviewProvider {
    static var previews: some View {

        PageView(imageData.map {ContentView(imageslide: $0)})
    }
}

我正在使用的JSON的样本。

   {
      "Imageslide":{
         "imagename":"MOS_SHB_1",
         "id":1001,
         "noButtons":0
      }
   },
   {
      "Imageslide":{
         "imagename":"MOS_SHB_2",
         "id":1002,
         "noButtons":0
      }
   },
   {
      "Imageslide":{
         "imagename":"MOS_SHB_3",
         "id":1003,
         "noButtons":1,
         "buttoninfo":[
            {
               "text":"The two halves of the arch touched for the first time. Workers riveted both top and bottom sections of the arch together, and the arch became self-supporting, allowing the support cables to be removed. On 20 August 1930 the joining of the arches was celebrated by flying the flags of Australia and the United Kingdom from the jibs of the creeper cranes.",
               "coords":[
                  -150,
                  220,
                  200,
                  200
               ]
            }
         ]
      }
   },
swift memory uikit swiftui uipageviewcontroller
1个回答
1
投票

创建 UIImage 照样

let imageModel = UIImage(contentsOfFile: imgPath)

并用于 body 作为示范 Image 观点

Image(uiImage: imageModel ?? UIImage())
© www.soinside.com 2019 - 2024. All rights reserved.