SwiftUI makes UIViewRepresentable flicker while updating

Issue

This Content is from Stack Overflow. Question asked by Artiom

I am rewriting the existing UIKit cell with SwiftUI. ChartView inside of it I wrapped with UIViewRepresentable because it is out of scope right now to rewrite it as well. The data for the chart I am getting async and update the view once I have it. The viewModel for the chart I marked as @Published so SwiftUI knows when to update the view. The problem is the view flickers once the update is there.

View:

struct DashboardWatchlistView: View {

    @ObservedObject var viewModel: DashboardWatchlistViewModel

    var body: some View {
        VStack(alignment: .center) {
            ZStack {
                HStack(spacing: 4) {
                    HStack(spacing: 16) {
                        Image(systemName: "person")

                        VStack(alignment: .leading, spacing: .designSystem(8) {
                            HStack {
                                Text(viewModel.leftTitle).font(.subheadline).fontWeight(.semibold).lineLimit(1)
                            }
                            Text(viewModel.leftSubtitle).font(.caption2).foregroundColor(Color(ThemeManager.current.neutral50))
                        }
                    }

                    Spacer()

                    if let chartViewModel = viewModel.chartViewModel {
                        AssetLineChartViewRepresentable(viewModel: chartViewModel).frame(width: 65, height: 20)
                    }

                    VStack(alignment: .trailing, spacing: .designSystem(.extraSmall3)) {
                        percentChangeView(assetPercentChangeType: viewModel.assetPercentChangeType)
                        Text(viewModel.rightSubtitle).font(.caption2).foregroundColor(Color(ThemeManager.current.neutral50))
                    }.padding(.leading, .designSystem(.medium))
                }

                .padding([.leading, .trailing])

                if !viewModel.hideSeparator {
                    VStack {
                        Spacer()
                        Divider().padding(.leading)
                    }
                }
            }
        }
    }
}

ViewModel:

final class DashboardWatchlistViewModel: ObservableObject {

    @Published var logoURL: URL?
    @Published var leftTitle = ""
    @Published var leftSubtitle = ""
    @Published var rightTitle = ""
    @Published var rightSubtitle = ""
    @Published var percentageChange: Decimal = .zero
    @Published var placeholderColor = ""
    @Published var corners: Corners = .none
    @Published var assetPercentChangeType: AssetPercentChangeType = .zero
    @Published var hideSeparator = false
    @Published var isHighlighted = false
    @Published var chartViewModel: AssetLineChartViewRepresentable.ViewModel?

    let chartDataEvent: AnyPublisher<BPChartData?, Never>

    init(tradable: Tradable,
         priceString: String,
         percentageChange: Decimal,
         corners: Corners,
         hideSeparator: Bool,
         assetPercentChangeType: AssetPercentChangeType,
         chartDataEvent: AnyPublisher<BPChartData?, Never>) {
             self.logoURL = tradable.logoURL
             self.leftTitle = tradable.name
             self.rightTitle = PercentageFormatter.default.string(from: abs(percentageChange))
             self.leftSubtitle = tradable.symbol
             self.rightSubtitle = priceString
             self.percentageChange = percentageChange
             self.placeholderColor = tradable.placeholderColor ?? ""
             self.corners = corners
             self.chartDataEvent = chartDataEvent
             self.hideSeparator = hideSeparator
             self.assetPercentChangeType = assetPercentChangeType
    }
}

Cell where I set the chartViewModel:

final class DashboardWatchlistCell: UICollectionViewCell, HostingCell {

    var hostingController: UIHostingController<DashboardWatchlistView>?
    private var dashboardWatchlistView: DashboardWatchlistView?
    private var viewModel: DashboardWatchlistViewModel?

    private var cancellables = Set<AnyCancellable>()

    override var isHighlighted: Bool {
        didSet {
            viewModel?.isHighlighted = isHighlighted
        }
    }

    func configure(with viewModel: DashboardWatchlistViewModel) {
        self.dashboardWatchlistView = DashboardWatchlistView(viewModel: viewModel)
        self.viewModel = viewModel

        // HERE I SET THE VIEWMODEL
        viewModel.chartDataEvent.compactMap { $0 }
        .receive(on: DispatchQueue.main)
        .sink { chartData in
            viewModel.chartViewModel = AssetLineChartViewRepresentable.ViewModel(chartData: chartData, percentageChange: viewModel.percentageChange)
        }
        .store(in: &cancellables)
    }

    func addHostedView(to parent: UIViewController) {
        guard let dashboardWatchlistView = dashboardWatchlistView else { return }

        addHostedView(dashboardWatchlistView, to: parent)

        backgroundColor = .clear
    }
}

enter image description here



Solution

This question is not yet answered, be the first one who answer using the comment. Later the confirmed answer will be published as the solution.

This Question and Answer are collected from stackoverflow and tested by JTuto community, is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

people found this article helpful. What about you?