似た機能のボタンの定義をまとめる
画面上に同じようなボタンを HStack や VStack で並べる機会は多いと思います。
ただ、同じモディファイアのボタンを並べる個数ぶん記述するのがひどく苦痛です。この場合、ボタンの外観を変更したくなった場合、ボタンの個数ぶんの変更をしなくてはなりません。
プログラマたるもの、なんとかこの状況を解決できないでしょうか。
本記事では、
- 改善前のコード ( 簡単な例 )
- 改善後のコード ( 簡単な例 )
と
- 改善前のコード ( 実践的な例 )
- 改善後のコード ( 実践的な例 )
を示します。
改善前のコード ( 簡単な例 )
3個のボタンがあって、内部的に A = 20, B = 30 という値が仕込んであります。これを、加算、減算、乗算するボタンを定義してみます。
下記のコードは何の工夫もないコードです。3個のボタンの外観を変えたくなったら、いちいちモディファイアの値を書き換えないといけませんし、なにより加算、減算、乗算のロジックがそこに書いてあるというところがイマイチです。
import SwiftUI
struct ContentView: View {
@State var ValueA: Int = 20
@State var ValueB: Int = 30
@State var Answer: Int = 0
var body: some View {
Spacer()
VStack {
Text( String( format: "A is %d", ValueA ) )
Text( String( format: "B is %d", ValueB ) )
Text( String( format: "Answer is %d", Answer ) )
}
Spacer()
VStack {
// 加算ボタン.
Button( action: {
Answer = ValueA + ValueB
}){
Text( "Button A + B" )
.font( .title3 )
.foregroundColor( .white )
}
.padding( 20.0 )
.background( .blue )
// 減算ボタン.
Button( action: {
Answer = ValueA - ValueB
}){
Text( "Button A - B" )
.font( .title3 )
.foregroundColor( .white )
}
.padding( 20.0 )
.background( .blue )
// 乗算ボタン.
Button( action: {
Answer = ValueA * ValueB
}){
Text( "Button A * B" )
.font( .title3 )
.foregroundColor( .white )
}
.padding( 20.0 )
.background( .blue )
}
Spacer()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
改善後のコード ( 簡単な例 )
上記のコードを改善します。ボタンといっても実は View です。独自のビューを定義します。
39〜77行目が独自のビューです。
今回は、独自のボタンのビューを MyButton という名前にします。コンストラクタに与える引数の中に、action_type という引数を定義し、この値を評価してボタンの中で何をするか場合分けします。
また、演算結果をコール側にもどすにはどうすればいいでしょうか。コンストラクタの引数を参照渡しできれば目的が達成できます。
具体的な手法としては、45行目の @Binding がミソです。ボタンの中で演算した結果をコンストラクタ引数に戻します。コールする側は変数名の前に $ をつけてコールします。
最後に、28〜30行目の3個のボタンの記述が極めて簡略化されていることに注目してください。
import SwiftUI
struct BTN_ACTION {
static let ADD = 0x01
static let SUB = 0x02
static let MUL = 0x04
}
struct ContentView: View {
@State var ValueA: Int = 20
@State var ValueB: Int = 30
@State var Answer: Int = 0
var body: some View {
Spacer()
VStack {
Text( String( format: "A is %d", ValueA ) )
Text( String( format: "B is %d", ValueB ) )
Text( String( format: "Answer is %d", Answer ) )
}
Spacer()
VStack {
MyButton( title: "Button A + B", action_type: BTN_ACTION.ADD, a: ValueA, b: ValueB, want: $Answer )
MyButton( title: "Button A - B", action_type: BTN_ACTION.SUB, a: ValueA, b: ValueB, want: $Answer )
MyButton( title: "Button A * B", action_type: BTN_ACTION.MUL, a: ValueA, b: ValueB, want: $Answer )
}
Spacer()
}
}
struct MyButton: View {
let title: String
let action_type: Int
var a: Int
var b: Int
@Binding var want: Int
var body: some View {
Button( action: {
if ( action_type == BTN_ACTION.ADD )
{
want = a + b
}
else if ( action_type == BTN_ACTION.SUB )
{
want = a - b
}
else if ( action_type == BTN_ACTION.MUL )
{
want = a * b
}
else
{
/* NOP */
}
}){
Text( title )
.font( .title3 )
.foregroundColor( .white )
}
.padding( 20.0 )
.background( .blue )
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
こうしておけば、ボタンの外観を変更したくなった場合、69〜74行目のところのモディファイアの値を変更すれば、MyButton をコールしている側に配置したボタンの外観がイッキに変わります。
改善前のコード ( 実践的な例 )
このコードは、下記の記事のコードですが、6個のボタンにいちいちモディファイアやアクションをベタ書きしています。もしボタンに改造を入れた場合に、ヌケやモレが出る可能性が非常に高いです。
import SwiftUI
struct STRUCT_RGBA {
var R: UInt8
var G: UInt8
var B: UInt8
var A: UInt8
}
struct ContentView: View {
// 画像データ.
let DATA_W = 256
let DATA_H = 256
@State var Buffer: UnsafeMutableRawPointer?
@State var DataRGBA: UnsafeMutablePointer<STRUCT_RGBA>?
// 画像データを画面に表示するためのグッズ.
@State var TheCGContext: CGContext? = nil
@State var TheUIImage: UIImage? = nil
// 画面に出力する際の縮尺.
@State var RateX = 1.0
@State var RateY = 1.0
let SCALE_MODE_NONE = 1
let SCALE_MODE_FIT = 2
let SCALE_MODE_FILL = 4
let STR_SCALE_MODE_NONE = "none"
let STR_SCALE_MODE_FIT = "fit"
let STR_SCALE_MODE_FILL = "fill"
@State var FlagScale: Int
@State var StrScaleMode: String
@State var UiImgW: CGFloat = 0.0
@State var UiImgH: CGFloat = 0.0
// ボタン関係の定数.
let BTN_CORNER_RADIUS = 5.0
let BTN_STROKE_LINE_WIDTH = 1.0
let BTN_TXT_PADDING = 5.0
let BTN_COLOR_FG = Color.white
let BTN_COLOR_BG = Color.blue
let BTN_FRAME_COLOR = Color.white
// パスによる枠線描画の定数.
let PATH_LINE_COLOR = Color.yellow
let PATH_LINE_WIDTH = 2.0
// 初期化コード.
init (){
FlagScale = SCALE_MODE_NONE
StrScaleMode = STR_SCALE_MODE_NONE
}
var body: some View {
ZStack{
// 最背面は背景グレーで塗りつぶす.
Rectangle()
.foregroundColor( .gray )
// 中層面で画像を表示する.
if let TheUIImageUnwrapped = TheUIImage {
let ui_img_w = CGFloat( DATA_W ) * RateX
let ui_img_h = CGFloat( DATA_H ) * RateY
if FlagScale == SCALE_MODE_NONE {
// 等倍描画する.
Image( uiImage: TheUIImageUnwrapped )
.resizable()
.frame( width: ui_img_w, height: ui_img_h )
}
else if FlagScale == SCALE_MODE_FIT {
// フィット描画する.
Image( uiImage: TheUIImageUnwrapped )
.resizable()
.scaledToFit()
.frame( width: ui_img_w, height: ui_img_h )
}
else if FlagScale == SCALE_MODE_FILL {
// フィル描画する.
Image( uiImage: TheUIImageUnwrapped )
.resizable()
.scaledToFill()
.frame( width: ui_img_w, height: ui_img_h )
}
else
{
// 等倍描画する.
Image( uiImage: TheUIImageUnwrapped )
.resizable()
.frame( width: ui_img_w, height: ui_img_h )
}
// 画像を囲む枠線を描画する.
Path{
path in
let xs = 0.0
let ys = 0.0
let xe = ui_img_w
let ye = ui_img_h
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( PATH_LINE_COLOR, lineWidth: PATH_LINE_WIDTH )
.frame( width: ui_img_w, height: ui_img_h )
// 現在の描画モードを表示する.
Text( String( format: "%@,%.1f*%.1f", StrScaleMode, ui_img_w, ui_img_h ) )
.foregroundColor( .white )
.font( .system( size: 12.0 ) )
}
else
{
// TheUIImage が適切に生成されていなければ何も表示しない.
Spacer()
}
// 最前面の画面上部にデバッグ情報を表示する.
VStack{
let scr_w = UIScreen.main.bounds.size.width
let scr_h = UIScreen.main.bounds.size.height
Text( String( format: "ScreenWH is %.1f*%.1f", scr_w, scr_h ))
.foregroundColor( .white )
Text( String( format: "DataWH is %d*%d", DATA_W, DATA_H ))
.foregroundColor( .white )
}
.frame( maxWidth: .infinity, maxHeight: .infinity, alignment: .top )
.padding( .top )
VStack{
HStack{
// 描画サイズ変更リクエストボタン.
Button( action : {
RateX = 0.5
RateY = 2.0
}) {
Text( "x0.5 x2.0" )
.padding( BTN_TXT_PADDING )
.foregroundColor( BTN_COLOR_FG )
.background( BTN_COLOR_BG )
}
.overlay(
RoundedRectangle( cornerRadius: BTN_CORNER_RADIUS )
.stroke( BTN_FRAME_COLOR, lineWidth: BTN_STROKE_LINE_WIDTH )
)
// 描画サイズ変更リクエストボタン.
Button( action : {
RateX = 2.0
RateY = 0.5
}) {
Text( "x2.0 x0.5" )
.padding( BTN_TXT_PADDING )
.foregroundColor( BTN_COLOR_FG )
.background( BTN_COLOR_BG )
}
.overlay(
RoundedRectangle( cornerRadius: BTN_CORNER_RADIUS )
.stroke( BTN_FRAME_COLOR, lineWidth: BTN_STROKE_LINE_WIDTH )
)
// 描画サイズ変更リクエストボタン.
Button( action : {
RateX = 1.0
RateY = 1.0
}) {
Text( "x1.0 x1.0" )
.padding( BTN_TXT_PADDING )
.foregroundColor( BTN_COLOR_FG )
.background( BTN_COLOR_BG )
}
.overlay(
RoundedRectangle( cornerRadius: BTN_CORNER_RADIUS )
.stroke( BTN_FRAME_COLOR, lineWidth: BTN_STROKE_LINE_WIDTH )
)
}
HStack{
// 等倍描画リクエストボタン.
Button( action : {
FlagScale = SCALE_MODE_NONE
StrScaleMode = STR_SCALE_MODE_NONE
}) {
Text( STR_SCALE_MODE_NONE )
.padding( BTN_TXT_PADDING )
.foregroundColor( BTN_COLOR_FG )
.background( BTN_COLOR_BG )
}
.overlay(
RoundedRectangle( cornerRadius: BTN_CORNER_RADIUS )
.stroke( BTN_FRAME_COLOR, lineWidth: BTN_STROKE_LINE_WIDTH )
)
// フィット描画リクエストボタン.
Button( action : {
FlagScale = SCALE_MODE_FIT
StrScaleMode = STR_SCALE_MODE_FIT
}) {
Text( STR_SCALE_MODE_FIT )
.padding( BTN_TXT_PADDING )
.foregroundColor( BTN_COLOR_FG )
.background( BTN_COLOR_BG )
}
.overlay(
RoundedRectangle( cornerRadius: BTN_CORNER_RADIUS )
.stroke( BTN_FRAME_COLOR, lineWidth: BTN_STROKE_LINE_WIDTH )
)
// フィル描画リクエストボタン.
Button( action : {
FlagScale = SCALE_MODE_FILL
StrScaleMode = STR_SCALE_MODE_FILL
}) {
Text( STR_SCALE_MODE_FILL )
.padding( BTN_TXT_PADDING )
.foregroundColor( BTN_COLOR_FG )
.background( BTN_COLOR_BG )
}
.overlay(
RoundedRectangle( cornerRadius: BTN_CORNER_RADIUS )
.stroke( BTN_FRAME_COLOR, lineWidth: BTN_STROKE_LINE_WIDTH )
)
}
}
.frame( maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom )
.padding( .bottom )
} // ZStack end.
.onAppear{
// メモリを確保する.
let numpix = DATA_W * DATA_H
let size = MemoryLayout<STRUCT_RGBA>.stride * numpix
let align = MemoryLayout<STRUCT_RGBA>.alignment
Buffer = UnsafeMutableRawPointer.allocate( byteCount: size, alignment: align )
// 連続メモリをRGBAの構造体の並びとして解釈する.
DataRGBA = Buffer?.bindMemory( to: STRUCT_RGBA.self, capacity: numpix )
// 確保したメモリに、ななめグラデーションデータを仕込む.
SetGradDataRGBA( DataRGBA!, DATA_W, DATA_H )
let one_scan_bytes = DATA_W * 4
// ビットマップコンテキストを作成する.
TheCGContext = CGContext(
data: DataRGBA, // ここは data: Buffer, としても同じ動作になる.
width: DATA_W,
height: DATA_H,
bitsPerComponent: 8,
bytesPerRow: one_scan_bytes,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
)
if TheCGContext != nil
{
let the_cgimage = TheCGContext!.makeImage()
if let the_cgimage = the_cgimage
{
TheUIImage = UIImage( cgImage: the_cgimage )
}
else
{
// デバッグコンソールに出力する.
print( "the_cgimage is nil.")
}
}
else
{
// デバッグコンソールに出力する.
print( "the_cgcontext is nil." )
}
}
.onDisappear {
// メモリを破棄する.
Buffer?.deallocate()
}
} // some View end.
// 画像にデータを仕込むメソッド.
func SetGradDataRGBA(
_ data: UnsafeMutablePointer<STRUCT_RGBA>,
_ width: Int,
_ height: Int
)->Void {
let w = width
let h = height
var value: UInt8 = 0
for j in 0 ..< h{
var adrs = w * j
for i in 0 ..< w{
let tmp = i + j
value = UInt8( tmp % 256 )
data[ adrs ].R = 0x00; // R
data[ adrs ].G = 0x00; // G
data[ adrs ].B = value; // B
data[ adrs ].A = 0xff; // A
adrs += 1
}
}
return
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ボタンに直接ロジックが書いてあり、よくありません。また、同じようなコードを何度も書いているのが退屈で苦痛です。
改善後のコード ( 実践的な例 )
ボタンのアクションや外観を MyButton という独自のビューを定義して、それを使うことでボタンまわりのコードの共通化をはかっています。
もしボタンの外観を変えたい場合は MyButton の中のモディファイアを変更することで、すべてのボタンの外観をイッキに変更することが可能です。
import SwiftUI
struct BTN_ACTION {
static let SCALE_MODE_NONE = 0x01
static let SCALE_MODE_FIT = 0x02
static let SCALE_MODE_FILL = 0x04
static let RATE_X10_Y10 = 0x10
static let RATE_X05_Y20 = 0x20
static let RATE_X20_Y05 = 0x40
}
struct BTN_TITLE {
static let NONE = "none"
static let FIT = "fit"
static let FILL = "fill"
static let X10_Y10 = "x1.0 x1.0"
static let X05_Y20 = "x0.5 x2.0"
static let X20_Y05 = "x2.0 x0.5"
}
struct MY_CONST {
static let SCALE_MODE_NONE = 1
static let SCALE_MODE_FIT = 2
static let SCALE_MODE_FILL = 4
static let STR_SCALE_MODE_NONE = "none"
static let STR_SCALE_MODE_FIT = "fit"
static let STR_SCALE_MODE_FILL = "fill"
}
struct STRUCT_RGBA {
var R: UInt8
var G: UInt8
var B: UInt8
var A: UInt8
}
struct ContentView: View {
// 画像データ.
let DATA_W = 256
let DATA_H = 256
@State var Buffer: UnsafeMutableRawPointer?
@State var DataRGBA: UnsafeMutablePointer<STRUCT_RGBA>?
// 画像データを画面に表示するためのグッズ.
@State var TheCGContext: CGContext? = nil
@State var TheUIImage: UIImage? = nil
// 画面に出力する際の縮尺.
@State var RateX = 1.0
@State var RateY = 1.0
@State var FlagScale: Int
@State var StrScaleMode: String
@State var UiImgW: CGFloat = 0.0
@State var UiImgH: CGFloat = 0.0
// パスによる枠線描画の定数.
let PATH_LINE_COLOR = Color.yellow
let PATH_LINE_WIDTH = 2.0
// 初期化コード.
init (){
FlagScale = MY_CONST.SCALE_MODE_NONE
StrScaleMode = MY_CONST.STR_SCALE_MODE_NONE
}
var body: some View {
ZStack{
// 最背面は背景グレーで塗りつぶす.
Rectangle()
.foregroundColor( .gray )
// 中層面で画像を表示する.
if let TheUIImageUnwrapped = TheUIImage {
let ui_img_w = CGFloat( DATA_W ) * RateX
let ui_img_h = CGFloat( DATA_H ) * RateY
if FlagScale == MY_CONST.SCALE_MODE_NONE {
// 等倍描画する.
Image( uiImage: TheUIImageUnwrapped )
.resizable()
.frame( width: ui_img_w, height: ui_img_h )
}
else if FlagScale == MY_CONST.SCALE_MODE_FIT {
// フィット描画する.
Image( uiImage: TheUIImageUnwrapped )
.resizable()
.scaledToFit()
.frame( width: ui_img_w, height: ui_img_h )
}
else if FlagScale == MY_CONST.SCALE_MODE_FILL {
// フィル描画する.
Image( uiImage: TheUIImageUnwrapped )
.resizable()
.scaledToFill()
.frame( width: ui_img_w, height: ui_img_h )
}
else
{
// 等倍描画する.
Image( uiImage: TheUIImageUnwrapped )
.resizable()
.frame( width: ui_img_w, height: ui_img_h )
}
// 画像を囲む枠線を描画する.
Path{
path in
let xs = 0.0
let ys = 0.0
let xe = ui_img_w
let ye = ui_img_h
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( PATH_LINE_COLOR, lineWidth: PATH_LINE_WIDTH )
.frame( width: ui_img_w, height: ui_img_h )
// 現在の描画モードを表示する.
Text( String( format: "%@,%.1f*%.1f", StrScaleMode, ui_img_w, ui_img_h ) )
.foregroundColor( .white )
.font( .system( size: 12.0 ) )
}
else
{
// TheUIImage が適切に生成されていなければ何も表示しない.
Spacer()
}
// 最前面の画面上部にデバッグ情報を表示する.
VStack{
let scr_w = UIScreen.main.bounds.size.width
let scr_h = UIScreen.main.bounds.size.height
Text( String( format: "ScreenWH is %.1f*%.1f", scr_w, scr_h ))
.foregroundColor( .white )
Text( String( format: "DataWH is %d*%d", DATA_W, DATA_H ))
.foregroundColor( .white )
}
.frame( maxWidth: .infinity, maxHeight: .infinity, alignment: .top )
.padding( .top )
VStack{
HStack{
// 描画サイズ変更リクエストボタン.
MyButton(
title: BTN_TITLE.X05_Y20,
action_type: BTN_ACTION.RATE_X05_Y20,
flag_scale: $FlagScale,
str_scale_mode: $StrScaleMode,
rate_x: $RateX,
rate_y: $RateY
)
// 描画サイズ変更リクエストボタン.
MyButton(
title: BTN_TITLE.X20_Y05,
action_type: BTN_ACTION.RATE_X20_Y05,
flag_scale: $FlagScale,
str_scale_mode: $StrScaleMode,
rate_x: $RateX,
rate_y: $RateY
)
// 描画サイズ変更リクエストボタン.
MyButton(
title: BTN_TITLE.X10_Y10,
action_type: BTN_ACTION.RATE_X10_Y10,
flag_scale: $FlagScale,
str_scale_mode: $StrScaleMode,
rate_x: $RateX,
rate_y: $RateY
)
}
HStack {
// 等倍描画リクエストボタン.
MyButton(
title: MY_CONST.STR_SCALE_MODE_NONE,
action_type: BTN_ACTION.SCALE_MODE_NONE,
flag_scale: $FlagScale,
str_scale_mode: $StrScaleMode,
rate_x: $RateX,
rate_y: $RateY
)
// フィット描画リクエストボタン.
MyButton(
title: MY_CONST.STR_SCALE_MODE_FIT,
action_type: BTN_ACTION.SCALE_MODE_FIT,
flag_scale: $FlagScale,
str_scale_mode: $StrScaleMode,
rate_x: $RateX,
rate_y: $RateY
)
// フィル描画リクエストボタン.
MyButton(
title: MY_CONST.STR_SCALE_MODE_FILL,
action_type: BTN_ACTION.SCALE_MODE_FILL,
flag_scale: $FlagScale,
str_scale_mode: $StrScaleMode,
rate_x: $RateX,
rate_y: $RateY
)
}
}
.frame( maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom )
.padding( .bottom )
} // ZStack end.
.onAppear{
// メモリを確保する.
let numpix = DATA_W * DATA_H
let size = MemoryLayout<STRUCT_RGBA>.stride * numpix
let align = MemoryLayout<STRUCT_RGBA>.alignment
Buffer = UnsafeMutableRawPointer.allocate( byteCount: size, alignment: align )
// 連続メモリをRGBAの構造体の並びとして解釈する.
DataRGBA = Buffer?.bindMemory( to: STRUCT_RGBA.self, capacity: numpix )
// 確保したメモリに、ななめグラデーションデータを仕込む.
SetGradDataRGBA( DataRGBA!, DATA_W, DATA_H )
let one_scan_bytes = DATA_W * 4
// ビットマップコンテキストを作成する.
TheCGContext = CGContext(
data: DataRGBA, // ここは data: Buffer, としても同じ動作になる.
width: DATA_W,
height: DATA_H,
bitsPerComponent: 8,
bytesPerRow: one_scan_bytes,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
)
if TheCGContext != nil
{
let the_cgimage = TheCGContext!.makeImage()
if let the_cgimage = the_cgimage
{
TheUIImage = UIImage( cgImage: the_cgimage )
}
else
{
// デバッグコンソールに出力する.
print( "the_cgimage is nil.")
}
}
else
{
// デバッグコンソールに出力する.
print( "the_cgcontext is nil." )
}
}
.onDisappear {
// メモリを破棄する.
Buffer?.deallocate()
}
} // some View end.
// 画像にデータを仕込むメソッド.
func SetGradDataRGBA(
_ data: UnsafeMutablePointer<STRUCT_RGBA>,
_ width: Int,
_ height: Int
)->Void {
let w = width
let h = height
var value: UInt8 = 0
for j in 0 ..< h{
var adrs = w * j
for i in 0 ..< w{
let tmp = i + j
value = UInt8( tmp % 256 )
data[ adrs ].R = 0x00; // R
data[ adrs ].G = 0x00; // G
data[ adrs ].B = value; // B
data[ adrs ].A = 0xff; // A
adrs += 1
}
}
return
}
}
struct MyButton: View {
let title: String
let action_type: Int
@Binding var flag_scale: Int
@Binding var str_scale_mode: String
@Binding var rate_x: Double
@Binding var rate_y: Double
var body: some View {
Button( action: {
if ( action_type == BTN_ACTION.SCALE_MODE_NONE )
{
flag_scale = MY_CONST.SCALE_MODE_NONE
str_scale_mode = MY_CONST.STR_SCALE_MODE_NONE
}
else if ( action_type == BTN_ACTION.SCALE_MODE_FIT )
{
flag_scale = MY_CONST.SCALE_MODE_FIT
str_scale_mode = MY_CONST.STR_SCALE_MODE_FIT
}
else if ( action_type == BTN_ACTION.SCALE_MODE_FILL )
{
flag_scale = MY_CONST.SCALE_MODE_FILL
str_scale_mode = MY_CONST.STR_SCALE_MODE_FILL
}
else if ( action_type == BTN_ACTION.RATE_X10_Y10 )
{
rate_x = 1.0
rate_y = 1.0
}
else if ( action_type == BTN_ACTION.RATE_X05_Y20 )
{
rate_x = 0.5
rate_y = 2.0
}
else if ( action_type == BTN_ACTION.RATE_X20_Y05 )
{
rate_x = 2.0
rate_y = 0.5
}
else
{
/* NOP */
}
})
{
Text( title )
.padding( 10.0 )
.foregroundColor( .white )
.background( .blue )
}
.overlay(
RoundedRectangle( cornerRadius: 5.0 )
.stroke( .white, lineWidth: 1.0 )
)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}