我在 MacOS 上使用 SwiftUI
Chart
绘制多个波形以进行比较。 我想通过右键单击使用 ImageRenderer
将任何特定波形保存为 PNG。
我已经可以保存图像,但生成的 PNG 文件的纵横比与视图中的图表不匹配。
这是完整视图:
保存的文件:
我尝试过
.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)
产生此输出图像。 (我在图像编辑器中手动添加了红色边框以显示边界)
如何使图像中的图表变大?
根据 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)
}
}