使用 ImageRenderer 绘制图表会给出方形、模糊的图像

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

我在 MacOS 上使用 SwiftUI

Chart
绘制多个波形以进行比较。 我想通过右键单击使用
ImageRenderer
将任何特定波形保存为 PNG。

我已经可以保存图像,但生成的 PNG 文件的纵横比与视图中的图表不匹配。

这是完整视图:

enter image description here

保存的文件:

enter image description here

我尝试过

.scale
属性,但它可以在各个方向上缩放。 有什么建议吗? 这是相关代码:

import SwiftUI
import Charts

let colorList =
[
    NSColor.blue,
    NSColor.red,
    NSColor.purple,
    NSColor.green
]

struct ButtonPlot : View
{
    private var mOneBtn : TOneBtn
    private var mXStart : Float = 0
    private var mXEnd   : Float = 0
    private var mTrain  : Int
    
    init(train: Int, btn: TOneBtn)
    {
        mTrain  = train
        mOneBtn = btn
        
        if let xs = mOneBtn.dataPointList.first?.x
        {
            mXStart = xs - 0.001
        }
        
        if let xe = mOneBtn.dataPointList.last?.x
        {
            mXEnd = xe + 0.001
        }
    }
    
    var body: some View
    {
        Text("Button \(mTrain):\(mOneBtn.idx). \(mOneBtn.dataPointList.count) data points")
        
        chartBlock
        .contextMenu
        {
            Button("Save")
            {
                let renderer = ImageRenderer(content: chartBlock)
                renderer.scale = 3.0
                
                print("proposed: \(renderer.proposedSize)")
                print("scale: \(renderer.scale)")
                
                if let nsImage = renderer.nsImage
                {
                    let imageRep = NSBitmapImageRep(data: nsImage.tiffRepresentation!)
                    if let pngData = imageRep?.representation(using: .png, properties: [:])
                    {
                        let url = URL(filePath: "/Users/danny/Desktop/snapshot.png")
                        
                        do
                        {
                            try pngData.write(to: url, options: .atomic)
                            print("WROTE FILE")
                        }
                        catch
                        {
                            print("Error saving \(url.path()) - \(error)")
                        }
                    }
                }
            }
        }
    }
    
    private var chartBlock : some View
    {
        Chart(mOneBtn.dataPointList)
        {
            LineMark(x: .value("x", $0.x), y: .value("y", $0.y))
        }
        .frame(maxWidth: 400, maxHeight: 100, alignment: .topLeading)
        .chartXScale(domain: mXStart ... mXEnd)
    }
    
}

struct BtnTrainView : View
{
    private let mTrain : TBtnTrain
    
    init(_ t: TBtnTrain)
    {
        mTrain = t
    }
    
    var body: some View
    {
        Text("BtnTrainView")
        VStack()
        {
            Text("Button Train \(mTrain.idx)")
            ForEach(mTrain.btnList, id: \.self)
            {
                btn in
                ButtonPlot(train: mTrain.idx, btn: btn)
            }
            Spacer()
        }
        .frame(alignment: .topLeading)
    }
}

struct MultiTrainView : View
{
    @EnvironmentObject private var mPlotter : DataPlotter
    
    var body: some View
    {
        ScrollView(.vertical, showsIndicators: true)
        {
            HStack()
            {
                ForEach(mPlotter.buttonTrainList)
                {
                    train in
                    BtnTrainView(train)
                }
            }
            .frame(alignment: .topLeading)
        }
        .frame(height: 500)
    }
}


struct GraphView: View
{
    @EnvironmentObject var   mPlotter : DataPlotter
    
    var body: some View
    {
        VStack
        {
            Text("\(mPlotter.buttonTrainList.count) Trains")
            
            MultiTrainView()
                .environmentObject(mPlotter)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
    }
}

编辑我

来自 Sweeper 的好建议 - 我没有意识到您可以在函数调用的参数列表中附加修饰符。

添加

.aspectRatio
修复了宽高比,但分辨率仍然很差。 我尝试添加
.frame
.scaleToFit()
.scaleEffect
和其他一些。 分辨率好多了,但输出图像的图形位于中心,周围有大片空白。 即,图像大小与
.frame
大小匹配,但其中的图表无法缩放。

例如这个:

let cb = chartBlock.frame(width: 1000, height: 500).aspectRatio(2, contentMode: .fit)
let renderer = ImageRenderer(content: cb)

产生此输出图像。 (我在图像编辑器中手动添加了红色边框以显示边界)

enter image description here

如何使图像中的图表变大?

macos swiftui
1个回答
0
投票

根据 Sweeper 的评论,我在代码中的各个位置使用了

.frame
参数。经过大量的试验和错误,这段代码产生了正确的输出:

struct ButtonPlot : View
{
    @EnvironmentObject private var mPlotter : DataPlotter
    
    private var mOneBtn : TOneBtn
    private var mXStart : Float = 0
    private var mXEnd   : Float = 0
    private var mTrain  : Int
    
    init(train: Int, btn: TOneBtn)
    {
        mTrain  = train
        mOneBtn = btn
        
        if let xs = mOneBtn.dataPointList.first?.x
        {
            mXStart = xs - 0.001
        }
        
        if let xe = mOneBtn.dataPointList.last?.x
        {
            mXEnd = xe + 0.001
        }
    }
    
    var body: some View
    {
        Text("Button \(mTrain):\(mOneBtn.idx). \(mOneBtn.dataPointList.count) data points")
        
        chartBlock
        .contextMenu
        {
            Button("Save")
            {
                let cb = chartBlock.aspectRatio(2, contentMode: .fit)
                let renderer = ImageRenderer(content: cb)
                
                if let nsImage = renderer.nsImage
                {
                    let imageRep = NSBitmapImageRep(data: nsImage.tiffRepresentation!)
                    if let pngData = imageRep?.representation(using: .png, properties: [:])
                    {
                        let url = URL(filePath: "/Users/danny/Desktop/snapshot.png")
                        
                        do
                        {
                            try pngData.write(to: url, options: .atomic)
                            print("WROTE FILE")
                        }
                        catch
                        {
                            print("Error saving \(url.path()) - \(error)")
                        }
                    }
                }
            }
        }
    }
    
    private var chartBlock : some View
    {
        Chart(mOneBtn.dataPointList)
        {
            LineMark(x: .value("x", $0.x), y: .value("y", $0.y))
        }
        .frame(width: 400, height: 100, alignment: .topLeading)
        .chartXScale(domain: mXStart ... mXEnd)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.