構造体配列による信号波形を画面いっぱいに描画する
この記事は下記の記事の発展形です。
上記の記事は画面の中の規定の範囲で波形を描画しておりますが、実際のところは iPhone の画面サイズぎりぎりで波形を表示したくなるものです。
iPhone は機種によって画面サイズが違うので ZStack が占めるサイズを動的に取得できないと画面いっぱいに波形を表示できません。こういった場合は GeometryReader という仕組みをつかいます。
取得したい画面内の要素を GeometryReader で囲んでやることで、そのサイズを取得できます。本記事では ZStack が占める領域を動的に取得したいので、ZStack の外側を GeometryReader で囲みます。
17行目にユーザが命名した変数 geom に ZStack が占めるサイズが入っています。それの geom.size.width と geom.size.height を参照すれば、ZStatck が占める幅と高さを取得できます。
26、27行目で、ZStack が占めるサイズを動的に取得しています。
32、62、82、106行目で、ZStack が占めるサイズを .frame モディファイアで指定しています。
import SwiftUI
struct X_FX {
var x: Double
var fx: Double
}
struct ContentView: View {
let NUM_DATA = 1000
@State var Data: [X_FX] = []
var body: some View {
GeometryReader {
geom in
ZStack{
// 全体背景の矩形を描画する.
Rectangle()
.foregroundColor( Color.mint )
// 画面のうち ZStack が占めるサイズ.
let RectW = geom.size.width
let RectH = geom.size.height
// 波形描画の背景の矩形を描画する.
Rectangle()
.foregroundColor( Color.black )
.frame( width: RectW, height: RectH )
// 波形を描画する.
Path{ path in
let amp = RectH * 0.5
let off_y = RectH * 0.5
if ( Data.count > 0 )
{
var plot_x = Double(0)
var plot_y = -( Data[0].fx ) * amp + off_y
var pnt = CGPoint( x: plot_x, y: plot_y )
path.move( to: pnt )
let loop = Int( RectW )
for i in 1 ..< loop {
let rate = Double(i)/Double( RectW - 1 )
let n = Int( rate * Double( Data.count - 1 ))
plot_x = Double(i)
plot_y = -( Data[n].fx ) * amp + off_y
pnt = CGPoint( x: plot_x, y: plot_y )
path.addLine( to: pnt )
}
}
}
.stroke( .green, lineWidth: 2.0 )
.frame( width: RectW, height: RectH ) // ここが重要.
// 目安になるセンター十字線を描画する.
Path{ path in
let plot_x = RectW * 0.5
let plot_y = RectH * 0.5
let pnt00 = CGPoint( x: plot_x, y: 0 )
let pnt01 = CGPoint( x: plot_x, y: RectH )
path.move( to: pnt00 )
path.addLine( to: pnt01 )
let pnt10 = CGPoint( x: 0, y: plot_y )
let pnt11 = CGPoint( x: RectW, y: plot_y )
path.move( to: pnt10 )
path.addLine( to: pnt11 )
}
.stroke( .white, lineWidth: 1.0 )
.frame( width: RectW, height: RectH ) // ここが重要.
// ZStack を占める Rectangle を囲む枠線を描画する.
Path{
path in
let xs = 0.0
let ys = 0.0
let xe = RectW
let ye = RectH
let pnt0 = CGPoint( x: xs, y: ys )
let pnt1 = CGPoint( x: xe, y: ys )
let pnt2 = CGPoint( x: xe, y: ye )
let pnt3 = CGPoint( x: xs, y: ye )
// 0, 1, 2, 3, 0 で矩形枠線を描画する.
path.move( to: pnt0 )
path.addLine(to: pnt1 )
path.addLine(to: pnt2 )
path.addLine(to: pnt3 )
path.addLine(to: pnt0 )
}
.stroke( .pink, lineWidth: 3.0 )
.frame( width: RectW, height: RectH ) // ここが重要.
// データをセットするボタンを水平に並べる.
HStack{
// シンク関数をセットするボタン.
Button( action: {
let _ = SetDataSinc( &Data, NUM_DATA )
}){
Text( "sinc" )
.foregroundColor( .white )
}
.padding()
.background( .blue )
// サイン関数をセットするボタン.
Button( action: {
let _ = SetDataSin( &Data, NUM_DATA )
}){
Text( "sin" )
.foregroundColor( .white )
}
.padding()
.background( .blue )
// コサイン関数データをセットするボタン.
Button( action: {
let _ = SetDataCos( &Data, NUM_DATA )
}){
Text( "cos" )
.foregroundColor( .white )
}
.padding()
.background( .blue )
}
.frame( maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom )
.padding( .bottom )
}
.onAppear(){
// 起動時にはシンク関数を描画する.
SetDataSinc( &Data, NUM_DATA )
}
}
}
func SetDataSinc( _ data: inout [X_FX], _ req_num_data: Int )->Void {
// いったん配列をクリアする.
data.removeAll()
let RANGE = 2.0
let MUL = 2.0
for n in 0 ..< req_num_data {
let rate = Double(n)/Double( req_num_data - 1)
var v = RANGE * rate
v -= 1.0 // この時点で -1.0 〜 0.0 〜 +1.0 になる.
let t = v * ( 2.0 * ( Double.pi )) * MUL
var ft = 0.0
// ゼロ割チェック.
if ( t != 0.0 )
{
ft = sin( t ) / t
}
else
{
ft = 1.0
}
// データ要素を追加する.
let element = X_FX( x: t, fx: ft )
data.append( element )
}
}
func SetDataSin( _ data: inout [X_FX], _ req_num_data: Int )->Void {
// いったん配列をクリアする.
data.removeAll()
let RANGE = 2.0
let MUL = 2.0
for n in 0 ..< req_num_data {
let rate = Double(n)/Double( req_num_data - 1)
var v = RANGE * rate
v -= 1.0 // この時点で -1.0 〜 0.0 〜 +1.0 になる.
let t = v * ( 2.0 * ( Double.pi )) * MUL
let ft = sin( t )
// データ要素を追加する.
let element = X_FX( x: t, fx: ft )
data.append( element )
}
}
func SetDataCos( _ data: inout [X_FX], _ req_num_data: Int )->Void {
// いったん配列をクリアする.
data.removeAll()
let RANGE = 2.0
let MUL = 2.0
for n in 0 ..< req_num_data {
let rate = Double(n)/Double( req_num_data - 1)
var v = RANGE * rate
v -= 1.0 // この時点で -1.0 〜 0.0 〜 +1.0 になる.
let t = v * ( 2.0 * ( Double.pi )) * MUL
let ft = cos( t )
// データ要素を追加する.
let element = X_FX( x: t, fx: ft )
data.append( element )
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}