【じっくりSw1ftUI36】実践編6〜第21章 SwiftUI のスタックとフレーム〜配置と位置とサイズ
さてと前回
で
TextビューやButtonビュー、Labelビュー
について紹介したので、前回実は既にしれっとやってる
StackビューやFrame
について触れてく〜〜〜!
今回もオイラの学びなんかが必要ないって人は、
を参照してね〜〜〜🕺
さてと、では早速、
じっくり第21章を読んでく👓
概要としては
まあ、ユーザーインターフェース(UI、操作画面)を幅広くデザインするために、
ボタンやラベル、スライダー、トグルみたいなビュー
をコンポーネントが含んでるってことが言いたいみたいんだね👀そんで、この章では、SwiftUIが含む
Stackコンテナー
で、簡単に相関的な操作画面を作る方法を説明してくんだけど、既にコンテナー自体は
で軽く紹介してるから、サイズとか振る舞いなんかをより詳細に説明してくってことが言いたいみたいだね👀
SwiftUIのスタックとしては、、、
VStack:縦(VerticalのV)
HStack:横(HorizontalのH)
ZStack:重層的に重ねる
がある👀
動かしながら読んでこう
まずは、前回感じで〜〜〜
import SwiftUI
struct Essentials21: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
Essentials21()
}
ScrollViewでコンテンツを全部囲んで〜〜〜
import SwiftUI
struct Essentials21: View {
var body: some View {
ScrollView{
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
}
#Preview {
Essentials21()
}
まずは、HStack
import SwiftUI
struct Essentials21: View {
var body: some View {
ScrollView{
HStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
}
}
}
#Preview {
Essentials21()
}
てな感じで、画像を横並びに配置
これを今度は縦に並べると〜〜〜
import SwiftUI
struct Essentials21: View {
var body: some View {
ScrollView{
VStack{
HStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
VStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
}
}
}
}
#Preview {
Essentials21()
}
スタックに埋め込みたいビューを右クリックして、埋め込みもできるみたいだね
import SwiftUI
struct Essentials21: View {
var body: some View {
ScrollView{
HStack {
Image(systemName: "arrowshape.right")
}
VStack{
HStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
VStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
}
}
}
}
#Preview {
Essentials21()
}
っと、ちょいここまでで少しコードが増えてきてるので〜〜〜
ここまで書いたコードを整理して
import SwiftUI
struct Essentials21: View {
var body: some View {
ScrollView{
VStack{
Essentials21OutlineView()
}
}
}
}
#Preview {
Essentials21()
}
struct Essentials21OutlineView: View {
var body: some View {
HStack {
Image(systemName: "arrowshape.right")
}
VStack{
HStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
VStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
}
}
}
てな感じでコードを小分けにしとく〜〜
より複雑な配置にしてみると、、、
struct Essentials21ComplexityView: View {
var body: some View {
VStack{
HStack{
Text("お会計総額")
Text("¥1050")
}
.font(.title)
HStack{
VStack{
Text("")
.font(.headline)
Text("Apple")
.font(.headline)
Text("Banana")
.font(.headline)
Text("Orange")
.font(.headline)
}
VStack{
Text("Menu")
Text("🍎")
Text("🍌")
Text("🟠")
}
VStack{
Text("単価")
Text("¥300")
Text("¥150")
Text("¥100")
}
VStack{
Text("個数")
Text("2")
Text("1")
Text("3")
}
VStack{
Text("小計")
Text("¥600")
Text("¥150")
Text("¥300")
}
}
}
}
}
で、
ギッチギチに詰まってるので、Spacerを使って〜〜
struct Essentials21ComplexityView: View {
var body: some View {
VStack{
HStack{
Text("お会計総額")
Text("¥1050")
}
.font(.title)
Spacer()
HStack{
VStack{
Text("")
.font(.headline)
Text("Apple")
.font(.headline)
Text("Banana")
.font(.headline)
Text("Orange")
.font(.headline)
}
//2節で追加
Spacer()
VStack{
Text("Menu")
Text("🍎")
Text("🍌")
Text("🟠")
}
//2節で追加
Spacer()
VStack{
Text("単価")
Text("¥300")
Text("¥150")
Text("¥100")
}
//2節で追加
Spacer()
VStack{
Text("個数")
Text("2")
Text("1")
Text("3")
}
//2節で追加
Spacer()
VStack{
Text("小計")
Text("¥600")
Text("¥150")
Text("¥300")
}
}
}
}
}
ってやると、
にはなるんだけど、
両端も気になる
のであれば、
struct Essentials21ComplexityView: View {
var body: some View {
VStack{
HStack{
Text("お会計総額")
Text("¥1050")
}
.font(.title)
Spacer()
HStack{
//2節で追加
Spacer()
VStack{
Text("")
.font(.headline)
Text("Apple")
.font(.headline)
Text("Banana")
.font(.headline)
Text("Orange")
.font(.headline)
}
//2節で追加
Spacer()
VStack{
Text("Menu")
Text("🍎")
Text("🍌")
Text("🟠")
}
//2節で追加
Spacer()
VStack{
Text("単価")
Text("¥300")
Text("¥150")
Text("¥100")
}
//2節で追加
Spacer()
VStack{
Text("個数")
Text("2")
Text("1")
Text("3")
}
//2節で追加
Spacer()
VStack{
Text("小計")
Text("¥600")
Text("¥150")
Text("¥300")
}
//2節で追加
Spacer()
}
}
}
}
コンテンツの最初と最後にも追加してあげて〜〜〜
大分、スッキリしてきたね🕺
でさらに〜〜〜文字の位置が気になるところalignmentで〜〜
struct Essentials21ComplexityView: View {
var body: some View {
VStack{
HStack{
Text("お会計総額")
Text("¥1050")
}
.font(.title)
Spacer()
HStack(alignment: .center){
//2節で追加
Spacer()
VStack(alignment: .listRowSeparatorLeading){
Text("")
.font(.headline)
Text("Apple")
.font(.headline)
Text("Banana")
.font(.headline)
Text("Orange")
.font(.headline)
}
//2節で追加
Spacer()
VStack{
Text("Menu")
Text("🍎")
Text("🍌")
Text("🟠")
}
//2節で追加
Spacer()
VStack{
Text("単価")
Text("¥300")
Text("¥150")
Text("¥100")
}
//2節で追加
Spacer()
VStack{
Text("個数")
Text("2")
Text("1")
Text("3")
}
//2節で追加
Spacer()
VStack{
Text("小計")
Text("¥600")
Text("¥150")
Text("¥300")
}
//2節で追加
Spacer()
}
}
}
}
てな感じで修正すると〜〜〜
Spacer()だけだと微調整が効かないところはPadding()を使って〜〜〜
struct Essentials21ComplexityView: View {
var body: some View {
VStack{
HStack{
Text("お会計総額")
Text("¥1050")
}
.font(.title)
.padding(15)
Spacer()
HStack(alignment: .center){
//2節で追加
Spacer()
VStack(alignment: .listRowSeparatorLeading){
Text("")
.font(.headline)
Text("Apple")
.font(.headline)
Text("Banana")
.font(.headline)
Text("Orange")
.font(.headline)
}
.padding(5)
//2節で追加
Spacer()
VStack{
Text("Menu")
Text("🍎")
Text("🍌")
Text("🟠")
}
.padding(5)
//2節で追加
Spacer()
VStack{
Text("単価")
Text("¥300")
Text("¥150")
Text("¥100")
}
//2節で追加
Spacer()
VStack{
Text("個数")
Text("2")
Text("1")
Text("3")
}
//2節で追加
Spacer()
VStack{
Text("小計")
Text("¥600")
Text("¥150")
Text("¥300")
}
//2節で追加
Spacer()
}
}
}
}
てな感じでPadding()を入れて〜〜〜
ひとつのビューで多くなりすぎる(10以上)と制限がかかるのでGroupを使って〜〜〜
struct Essentials21GroupedView:View {
var body: some View {
VStack{
Text("和暦 月名")
VStack{
Group{
Text("睦月")
Text("如月")
Text("弥生")
Text("卯月")
Text("皐月")
Text("水無月")
Text("文月")
Text("葉月")
Text("長月")
Text("神無月")
}
Group{
Text("霜月")
Text("師走")
}
}
}
}
}
Groupで囲ってあげるとエラーが出なくて済む
(最近のオイラの環境だと、このエラー出ないんだけどね👀💦
前は出てたから知ってて損はない奴)
動的にStackコンテナーを変更したいなら〜〜〜
struct Essentials21DynamicallyView : View {
@State var dynamicLayout: AnyLayout = AnyLayout(VStackLayout())
var body: some View {
VStack{
HStack{
Button(action:{
dynamicLayout = AnyLayout(HStackLayout())}){
Text("横向き")
}
Button(action:{
dynamicLayout = AnyLayout(VStackLayout())}){
Text("縦向き")
}
}
.font(.largeTitle)
dynamicLayout{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
}
}
てな感じのコードで〜〜〜
テキストの行数を指定したいとき
struct Essentials21LinelimitedView: View {
var body: some View {
HStack{
Image(systemName: "apple.logo")
Text("Apple.Inc")
Text("UnitedStatesAmerica")
}
.font(.largeTitle)
}
}
みたいにやって実行すると〜〜〜
struct Essentials21LinelimitedView: View {
var body: some View {
HStack{
Image(systemName: "apple.logo")
Text("Apple.Inc")
Text("UnitedStatesAmerica")
}
.font(.largeTitle)
.lineLimit(1)
}
}
てな感じでLineLimit()モディファイアを追加してあげると〜〜〜
カールテールちゃんでお尻が…みたいになるくらいなら、そもそも
こんなに長いモノを指定してなけりゃいいのでは?👀
って思うかもしれないけど、最近やたら増えてる
本来の会社名はやたら長くなってんのに、そこをアルファベット3文字で短縮してる屋号と、正式な名称をコピーライトのところに載せてくださいって言われたらどうする?👀💦
って話
struct Essentials21LinelimitedView: View {
var body: some View {
HStack{
Image(systemName: "car")
Text("有限会社JULK")
Text("自動車運転者をこよなく愛する会")
}
.font(.largeTitle)
}
}
こんくらいな感じでも、使わないと
ただし、LineLimit()でやっても
そんな時には、オイラなら
「え?ロゴと屋号の下の行に正式名称で分けていいですか?」
って聞いて、
struct Essentials21LinelimitedView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "car")
Text("有限会社JULK")
}
.font(.largeTitle)
.lineLimit(1)
Text("自動車運転者をこよなく愛する会")
.font(.caption)
}
}
}
こんな感じにして
色までつけて
struct Essentials21LinelimitedView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "car")
.foregroundColor(.yellow)
Text("有限会社JULK")
.foregroundStyle(Color.green)
}
.font(.largeTitle)
.lineLimit(1)
Text("自動車運転者をこよなく愛する会")
.font(.caption)
.foregroundStyle(Color.blue)
}
}
}
優先順位で〜〜〜
struct Essentials21LinelimitedView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "car")
.foregroundColor(.yellow)
Text("有限会社JULK")
.foregroundStyle(Color.green)
Text("自動車運転者をこよなく愛する会")
.font(.caption)
.foregroundStyle(Color.blue)
.layoutPriority(1)
}
.font(.largeTitle)
.lineLimit(1)
}
}
}
できなくはないんだけど、
他が隠れてしまうと、ユーザーさんにはそこに何が書いてるか分からないし、他の文字が隠れてるのに画面の検証もしてないのかしら?🧐バグかなとしか受け取られない
UI/UXデザインとしてあり得ない
からね。
👉知っておいて損はなくても、こんな時は
クライアントが何を言ってきても他の方法を探した方がいい
「会社のロゴマークとかコピーライトの部分が、1行から2行に増えます」
ってだけで
けしからん!!!!
伝えたいところは隠れても構わんから1行にせよ!!!
とかゆー人はまず居ないでしょ。てかいたら逆に見てみたい
別に
struct Essentials21LinelimitedView: View {
var body: some View {
HStack{
Image(systemName: "car")
.foregroundColor(.yellow)
.font(.largeTitle)
VStack{
Text("有限会社JULK")
.foregroundStyle(Color.green)
Text("自動車運転者をこよなく愛する会")
.font(.caption)
.foregroundStyle(Color.blue)
.layoutPriority(1)
}
.font(.largeTitle)
.lineLimit(1)
}
}
}
ロゴマークとかアプリは何のために作ってるのか?
って根本を考えれば分かるでしょ。そう、
ユーザーさんに訴求する
ためだし、そのために、
専門知識をあまり持っていないクライアントさんをうまく導く
が仕事だからね〜〜〜
他にも遅延スタックってものあるみたいだけど、
ここではサンプルコードも載ってないし、冒頭のリンクの解説だけ貼っとこ👀
"この章ではこれまで、HStack、VStack、および ZStack ビューについてのみ説明してきました。これまでに示したスタックの例には比較的少数の子ビューが含まれていますが、スタックに大量のビューが含まれる可能性があります。これは、スタックが ScrollView に埋め込まれている場合に特に一般的です。 ScrollView は、ユーザーが、それを含むビューまたはデバイスの画面の表示領域を超えて広がるコンテンツをスクロールできるようにするビューです。
従来の HStack ビューと VStack ビューを使用する場合、システムは、それらのビューが現在ユーザーに表示されているかどうかに関係なく、初期化時にすべてのビューの子ビューを作成します。これはほとんどの要件では問題になりませんが、スタックに数千の子ビューがある状況ではパフォーマンスの低下につながる可能性があります。
この問題に対処するために、SwiftUI は「遅延」垂直および水平スタック ビューも提供します。これらのビュー (LazyVStack および LazyHStack という名前) は、従来のスタック ビューとまったく同じ宣言構文を使用しますが、必要な場合にのみ子ビューを作成するように設計されています。たとえば、ユーザーがスタックをスクロールすると、現在画面外にあるビューは、ユーザーに表示される時点に近づいたときにのみ作成されます。これらのビューが表示領域の外に出ると、SwiftUI はそれらのビューを解放して、システム リソースを占有しないようにします。
従来のスタックと遅延スタックのどちらを使用するかを決定するときは、通常、従来のスタックの使用を開始し、多数の子ビューに関連するパフォーマンスの問題が発生した場合は遅延スタックに切り替えることをお勧めします。"
だそうな👀要は、
遅延が発生しそうな場合は、遅延型のLaztStackを使おうね
ってことが言いたいみたいだけど、Swiftでそんなに遅延が発生するってよっぽど子ビューが何かをやらかしてるから、そもそもの
アプリ設計を見直すか
一度に表示させるビューを削るかまとめる
のどちらかにした方がいいとは思うけどね👀
さてと、次はフレームちゃん
例えば、さっきから頻繁にスクショに出てくるAppleマークがデカすぎてうざいなって時なんかに
struct Essentials21DynamicallyView : View {
@State var dynamicLayout: AnyLayout = AnyLayout(VStackLayout())
var body: some View {
VStack{
HStack{
Button(action:{
dynamicLayout = AnyLayout(HStackLayout())}){
Text("横向き")
}
Button(action:{
dynamicLayout = AnyLayout(VStackLayout())}){
Text("縦向き")
}
}
.font(.largeTitle)
dynamicLayout{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加
.frame(width: 100, height: 100)
}
}
}
}
って感じでしてあげると
サイズを細かく指定したいとき:frameモディファイア
を使うってだけの話。
さらに細かくalignmentで指定してくと〜〜〜
//7節で追加
struct Essentials21FramingView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
}
}
}
}
てなコードで
//7節で追加
struct Essentials21FramingView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottom)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomLeading)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomTrailing)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .center)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerFirstTextBaseline)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerLastTextBaseline)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leading)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingFirstTextBaseline)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingLastTextBaseline)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .top)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topLeading)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topTrailing)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailing)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingFirstTextBaseline)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingLastTextBaseline)
}
}
}
}
てな感じでalignmentを追加して〜〜
さらに線で囲むと〜〜〜
//7節で追加
struct Essentials21FramingView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottom)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomLeading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomTrailing)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .center)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerLastTextBaseline)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingLastTextBaseline)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .top)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topLeading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topTrailing)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailing)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingLastTextBaseline)
.border(Color.black)
}
}
}
}
最大と最小のサイズを指定
さらに〜〜〜
struct Essentials21DynamicallyView : View {
@State var dynamicLayout: AnyLayout = AnyLayout(VStackLayout())
var body: some View {
VStack{
HStack{
Button(action:{
dynamicLayout = AnyLayout(HStackLayout())}){
Text("横向き")
}
Button(action:{
dynamicLayout = AnyLayout(VStackLayout())}){
Text("縦向き")
}
}
.font(.largeTitle)
dynamicLayout{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加- 最大と最小を具体的に指定
.frame(minWidth: 100,maxWidth: 300,minHeight: 100,maxHeight: 300)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加-最大と最小を画面サイズに合わせて可変に指定
.frame(minWidth: 0,maxWidth: .infinity,minHeight: 0,maxHeight: .infinity)
}
}
}
}
みたいな感じで〜〜
でできなくはないんだけど、
Maxに.infinity
を使った場合はもはやaspectRatio.fitと同じにしか見えないので〜〜〜あくまでも、
frameでもこーゆー使い方もあるんだ!
で参考にしてみてね〜〜〜
状況によりけりなので、色々動かしてみてね
SafeAreaを無視したいとき
struct Essentials21: View {
var body: some View {
ScrollView{
VStack{
Essentials21OutlineView()
Essentials21ComplexityView()
Essentials21GroupedView()
Essentials21DynamicallyView()
Essentials21LinelimitedView()
Essentials21FramingView()
}
}
//7節で追加
.ignoresSafeArea()
}
}
てな感じで指定すると〜〜〜
って感じで、Wifiなんかを表示してるセーフエリアを無視して、本当に画面全体で表示をすることもできる👀
相対的なコンテナのサイズでサイズを指定したい-GeometryReader
struct Essentials21GeometryReadingView: View {
var body: some View {
GeometryReader{ geometry in
VStack{
HStack{
Image(systemName: "apple.logo")
.foregroundColor(.yellow)
.font(.largeTitle)
Text("Apple")
.foregroundStyle(Color.green)
}
.font(.largeTitle)
.frame(
width: geometry.size.width/2,
height:(geometry.size.height/4)*3
)
HStack{
Image(systemName: "apple.logo")
.foregroundColor(.red)
.font(.largeTitle)
Text("Apple")
.foregroundStyle(Color.black)
}
.font(.largeTitle)
.frame(
width: geometry.size.width/3,
height:(geometry.size.height/4)
)
}
}
}
}
てな感じにしてさらに位置を調整できなくもない。
ここまで来ると、個人的にはもはやSwiftUIの旨みって何だっけ🧐
ってなっちゃうけどね💦
本編は以上。
さてと、軽くいつものよもやま話
GeometryReaderは知ってはいるけど、
コードの可読性も落ちる=バグの原因にもなりかねない
ので、
使うべき時しか使わない
くらいがちょうどいい
より職人っぽいから〜〜とかより難しいことをやってそう
ってイメージだけで、他で簡単に実装ができるビューにわざわざやってると、
要件追加とか仕様変更で全てのビューを変更しないといけないって時に
改修に手間がかかるようになるけどどーすんの?🤔
って話。
オイラは正直、テキストビューとかイメージをこんな感じで重ねたい
ってなったことは一度もないし、こんなん使わなくても、
ZStackとか元々ロゴが重なってるイメージごと貼り付ければ、
似たようなことは出来るぞ
なので〜〜〜!
今回のコードまとめ
import SwiftUI
struct Essentials21: View {
var body: some View {
ScrollView{
VStack{
Essentials21OutlineView()
Essentials21ComplexityView()
Essentials21GroupedView()
Essentials21DynamicallyView()
Essentials21LinelimitedView()
Essentials21FramingView()
Essentials21GeometryReadingView()
}
}
//7節で追加
.ignoresSafeArea()
}
}
#Preview {
Essentials21()
}
struct Essentials21OutlineView: View {
var body: some View {
HStack {
Image(systemName: "arrowshape.right")
}
VStack{
HStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
VStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
}
}
}
struct Essentials21ComplexityView: View {
var body: some View {
VStack{
HStack{
Text("お会計総額")
Text("¥1050")
}
.font(.title)
.padding(15)
Spacer()
HStack(alignment: .center){
//2節で追加
Spacer()
VStack(alignment: .listRowSeparatorLeading){
Text("")
.font(.headline)
Text("Apple")
.font(.headline)
Text("Banana")
.font(.headline)
Text("Orange")
.font(.headline)
}
.padding(5)
//2節で追加
Spacer()
VStack{
Text("Menu")
Text("🍎")
Text("🍌")
Text("🟠")
}
.padding(5)
//2節で追加
Spacer()
VStack{
Text("単価")
Text("¥300")
Text("¥150")
Text("¥100")
}
//2節で追加
Spacer()
VStack{
Text("個数")
Text("2")
Text("1")
Text("3")
}
//2節で追加
Spacer()
VStack{
Text("小計")
Text("¥600")
Text("¥150")
Text("¥300")
}
//2節で追加
Spacer()
}
}
}
}
struct Essentials21GroupedView:View {
var body: some View {
VStack{
Text("和暦 月名")
VStack{
Group{
Text("睦月")
Text("如月")
Text("弥生")
Text("卯月")
Text("皐月")
Text("水無月")
Text("文月")
Text("葉月")
Text("長月")
Text("神無月")
}
Group{
Text("霜月")
Text("師走")
}
}
}
}
}
struct Essentials21DynamicallyView : View {
@State var dynamicLayout: AnyLayout = AnyLayout(VStackLayout())
var body: some View {
VStack{
HStack{
Button(action:{
dynamicLayout = AnyLayout(HStackLayout())}){
Text("横向き")
}
Button(action:{
dynamicLayout = AnyLayout(VStackLayout())}){
Text("縦向き")
}
}
.font(.largeTitle)
dynamicLayout{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加- 最大と最小を具体的に指定
.frame(minWidth: 100,maxWidth: 300,minHeight: 100,maxHeight: 300)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加-最大と最小を画面サイズに合わせて可変に指定
.frame(minWidth: 0,maxWidth: .infinity,minHeight: 0,maxHeight: .infinity)
}
}
}
}
struct Essentials21LinelimitedView: View {
var body: some View {
HStack{
Image(systemName: "car")
.foregroundColor(.yellow)
.font(.largeTitle)
VStack{
Text("有限会社JULK")
.foregroundStyle(Color.green)
Text("自動車運転者をこよなく愛する会")
.font(.caption)
.foregroundStyle(Color.blue)
.layoutPriority(1)
}
.font(.largeTitle)
}
}
}
//7節で追加
struct Essentials21FramingView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottom)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomLeading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomTrailing)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .center)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerLastTextBaseline)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingLastTextBaseline)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .top)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topLeading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topTrailing)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailing)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingLastTextBaseline)
.border(Color.black)
}
}
}
}
struct Essentials21GeometryReadingView: View {
var body: some View {
GeometryReader{ geometry in
VStack{
HStack{
Image(systemName: "apple.logo")
.foregroundColor(.yellow)
.font(.largeTitle)
Text("Apple")
.foregroundStyle(Color.green)
}
.font(.largeTitle)
.frame(
width: geometry.size.width/2,
height:(geometry.size.height/4)*3
)
HStack{
Image(systemName: "apple.logo")
.foregroundColor(.red)
.font(.largeTitle)
Text("Apple")
.foregroundStyle(Color.black)
}
.font(.largeTitle)
.frame(
width: geometry.size.width/3,
height:(geometry.size.height/4)
)
}
}
}
}
Apple公式
さてと次回は、
でやった
プロパティラッパーをSwiftUIフレームワークで使う実践編の入り口
第22章:SwiftUI の状態プロパティ、監視可能な状態、および環境オブジェクト
をやってく〜〜〜🕺
記事公開後
いつもどおり
でやった作業をやってく🕺
サンプルコード
◾️EssentialsMenu.swift
//フレームワーク
import SwiftUI
import WebKit
//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentials: Identifiable {
var id: Int
var title: String
var view: ViewEnumiOSApp17DevelopmentEssentials
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentials {
case Ch1
//じっくり13で追加
case Ch2
//じっくり14で追加
case Ch3
//じっくり15で追加
case Ch4
//じっくり16で追加
case Ch5
//じっくり17で追加
case Ch6
//じっくり18で追加
case Ch7
//じっくり19で追加
case Ch8
//じっくり20、21で追加
case Ch9
//じっくり22、23で追加
case Ch10
//じっくり24で追加
case Ch11
//じっくり25で追加
case Ch12
//じっくり26で追加
case Ch13
//じっくり27,28で追加
case Ch14
//じっくり29で追加
case Ch15
//じっくり31で追加
case Ch16
//じっくり32で追加
case Ch17
//じっくり33で追加
case Ch18
//じっくり34で追加
case Ch19
//じっくり35で追加
case Ch20
//じっくり36で追加
case Ch21
}
//各項目に表示する文字列
let dataiOSApp17DevelopmentEssentials: [ListiOSApp17DevelopmentEssentials] = [
ListiOSApp17DevelopmentEssentials(id: 1, title: essentialsChapter1Title, view: .Ch1),
//じっくり13で追加
ListiOSApp17DevelopmentEssentials(id: 2, title: essentialsChapter2Title, view: .Ch2),
//じっくり13で追加
ListiOSApp17DevelopmentEssentials(id: 3, title: essentialsChapter3Title, view: .Ch3),
//じっくり15で追加
ListiOSApp17DevelopmentEssentials(id: 4, title: essentialsChapter4Title, view: .Ch4),
//じっくり16で追加
ListiOSApp17DevelopmentEssentials(id: 5, title: essentialsChapter5Title, view: .Ch5),
//じっくり17で追加
ListiOSApp17DevelopmentEssentials(id: 6, title: essentialsChapter6Title, view: .Ch6),
//じっくり18で追加
ListiOSApp17DevelopmentEssentials(id: 7, title: essentialsChapter7Title, view: .Ch7),
//じっくり19で追加
ListiOSApp17DevelopmentEssentials(id: 8, title: essentialsChapter8Title, view: .Ch8),
//じっくり20、21で追加
ListiOSApp17DevelopmentEssentials(id: 9, title: essentialsChapter9Title, view: .Ch9),
//じっくり22、23で追加
ListiOSApp17DevelopmentEssentials(id: 10, title: essentialsChapter10Title, view: .Ch10),
//じっくり24で追加
ListiOSApp17DevelopmentEssentials(id: 11, title: essentialsChapter11Title, view: .Ch11),
//じっくり25で追加
ListiOSApp17DevelopmentEssentials(id: 12, title: essentialsChapter12Title, view: .Ch12),
//じっくり26で追加
ListiOSApp17DevelopmentEssentials(id: 13, title: essentialsChapter13Title, view: .Ch13),
//じっくり27,28で追加
ListiOSApp17DevelopmentEssentials(id: 14, title: essentialsChapter14Title, view: .Ch14),
//じっくり29で追加
ListiOSApp17DevelopmentEssentials(id: 15, title: essentialsChapter15Title, view: .Ch15),
//じっくり31で追加
ListiOSApp17DevelopmentEssentials(id: 16, title: essentialsChapter16Title, view: .Ch16),
//じっくり32で追加
ListiOSApp17DevelopmentEssentials(id: 17, title: essentialsChapter17Title, view: .Ch17),
//じっくり33で追加
ListiOSApp17DevelopmentEssentials(id: 18, title: essentialsChapter18Title, view: .Ch18),
//じっくり34で追加
ListiOSApp17DevelopmentEssentials(id: 19, title: essentialsChapter19Title, view: .Ch19),
//じっくり35で追加
ListiOSApp17DevelopmentEssentials(id: 20, title: essentialsChapter20Title, view: .Ch20),
//じっくり36で追加
ListiOSApp17DevelopmentEssentials(id: 21, title: essentialsChapter21Title, view: .Ch21),
]
struct iOSApp17DevelopmentEssentials: View {
var body: some View {
VStack {
Divider()
List (dataiOSApp17DevelopmentEssentials) { data in
self.containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: data)
}
.edgesIgnoringSafeArea([.bottom])
}
.navigationTitle("iOS開発の章目次")
.navigationBarTitleDisplayMode(.inline)
}
//タップ後に遷移先へ遷移させる関数
func containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: ListiOSApp17DevelopmentEssentials) -> AnyView {
switch dataiOSApp17DevelopmentEssentials.view {
case .Ch1:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh1()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり13で追加
case .Ch2:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh2()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり13で追加
case .Ch3:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh3()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり15で追加
case .Ch4:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh4()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり16で追加
case .Ch5:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh5()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり17で追加
case .Ch6:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh6()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり18で追加
case .Ch7:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh7()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり19で追加
case .Ch8:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh8()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり20、21で追加
case .Ch9:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh9()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり22、23で追加
case .Ch10:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh10()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり24で追加
case .Ch11:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh11()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり25で追加
case .Ch12:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh12()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり26で追加
case .Ch13:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh13()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり27,28で追加
case .Ch14:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh14()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり29で追加
case .Ch15:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh15()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり31で追加
case .Ch16:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh16()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり32で追加
case .Ch17:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh17()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり33で追加
case .Ch18:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh18()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり34で追加
case .Ch19:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh19()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり35で追加
case .Ch20:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh20()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
//じっくり36で追加
case .Ch21:
return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh21()) {
Text(dataiOSApp17DevelopmentEssentials.title)
})
}
}
}
#Preview {
iOSApp17DevelopmentEssentials()
}
◾️Essentials21.swift
import SwiftUI
import WebKit
//iOSApp17DevelopmentEssentialsCh20.swift
//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh21: Identifiable {
var id: Int
var title: String
var view: ViewEnumiOSApp17DevelopmentEssentialsCh21
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh21 {
case Sec1
}
//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh21: [ListiOSApp17DevelopmentEssentialsCh21] = [
ListiOSApp17DevelopmentEssentialsCh21(id: 1, title: essentialsChapter21SubTitle, view: .Sec1)
]
struct iOSApp17DevelopmentEssentialsCh21: View {
var body: some View {
VStack {
Divider()
List (dataiOSApp17DevelopmentEssentialsCh21) { data in
self.containedViewiOSApp17DevelopmentEssentialsCh21(dataiOSApp17DevelopmentEssentialsCh21: data)
}
.edgesIgnoringSafeArea([.bottom])
}
.navigationTitle(essentialsChapter21NavigationTitle)
.navigationBarTitleDisplayMode(.inline)
}
//タップ後に遷移先へ遷移させる関数
func containedViewiOSApp17DevelopmentEssentialsCh21(dataiOSApp17DevelopmentEssentialsCh21: ListiOSApp17DevelopmentEssentialsCh21) -> AnyView {
switch dataiOSApp17DevelopmentEssentialsCh21.view {
case .Sec1:
return AnyView(NavigationLink (destination: Essentials21()) {
Text(dataiOSApp17DevelopmentEssentialsCh21.title)
})
}
}
}
#Preview {
iOSApp17DevelopmentEssentialsCh21()
}
//Essentials4.swift
struct Essentials21: View {
var body: some View {
VStack{
TabView {
Essentials21Contents()
.tabItem {
Image(systemName: contentsImageTab)
Text(contentsTextTab)
}
Essentials21Code()
.tabItem {
Image(systemName: codeImageTab)
Text(codeTextTab)
}
Essentials21Points()
.tabItem {
Image(systemName: pointImageTab)
Text(pointTextTab)
}
Essentials21WEB()
.tabItem {
Image(systemName: webImageTab)
Text(webTextTab)
}
}
}
}
}
#Preview {
Essentials21()
}
struct Essentials21Code: View {
var body: some View {
ScrollView{
Text(codeEssentials21)
}
}
}
#Preview {
Essentials21Code()
}
struct Essentials21Points: View {
var body: some View {
ScrollView{
Text(pointEssentials21)
}
}
}
#Preview {
Essentials21Points()
}
struct Essentials21WebView: UIViewRepresentable {
let searchURL: URL
func makeUIView(context: Context) -> WKWebView {
let view = WKWebView()
let request = URLRequest(url: searchURL)
view.load(request)
return view
}
func updateUIView(_ uiView: WKWebView, context: Context) {
}
}
struct Essentials21WEB: View {
private var url:URL = URL(string: urlEssentials21)!
var body: some View {Essentials4WebView(searchURL: url)
}
}
#Preview {
Essentials21WEB()
}
//コード
let codeEssentials21 = """
struct Essentials21Contents: View {
var body: some View {
ScrollView{
VStack{
Essentials21OutlineView()
Essentials21ComplexityView()
Essentials21GroupedView()
Essentials21DynamicallyView()
Essentials21LinelimitedView()
Essentials21FramingView()
Essentials21GeometryReadingView()
}
}
//7節で追加
.ignoresSafeArea()
}
}
#Preview {
Essentials21()
}
struct Essentials21OutlineView: View {
var body: some View {
HStack {
Image(systemName: "arrowshape.right")
}
VStack{
HStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
VStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
}
}
}
struct Essentials21ComplexityView: View {
var body: some View {
VStack{
HStack{
Text("お会計総額")
Text("¥1050")
}
.font(.title)
.padding(15)
Spacer()
HStack(alignment: .center){
//2節で追加
Spacer()
VStack(alignment: .listRowSeparatorLeading){
Text("")
.font(.headline)
Text("Apple")
.font(.headline)
Text("Banana")
.font(.headline)
Text("Orange")
.font(.headline)
}
.padding(5)
//2節で追加
Spacer()
VStack{
Text("Menu")
Text("🍎")
Text("🍌")
Text("🟠")
}
.padding(5)
//2節で追加
Spacer()
VStack{
Text("単価")
Text("¥300")
Text("¥150")
Text("¥100")
}
//2節で追加
Spacer()
VStack{
Text("個数")
Text("2")
Text("1")
Text("3")
}
//2節で追加
Spacer()
VStack{
Text("小計")
Text("¥600")
Text("¥150")
Text("¥300")
}
//2節で追加
Spacer()
}
}
}
}
struct Essentials21GroupedView:View {
var body: some View {
VStack{
Text("和暦 月名")
VStack{
Group{
Text("睦月")
Text("如月")
Text("弥生")
Text("卯月")
Text("皐月")
Text("水無月")
Text("文月")
Text("葉月")
Text("長月")
Text("神無月")
}
Group{
Text("霜月")
Text("師走")
}
}
}
}
}
struct Essentials21DynamicallyView : View {
@State var dynamicLayout: AnyLayout = AnyLayout(VStackLayout())
var body: some View {
VStack{
HStack{
Button(action:{
dynamicLayout = AnyLayout(HStackLayout())}){
Text("横向き")
}
Button(action:{
dynamicLayout = AnyLayout(VStackLayout())}){
Text("縦向き")
}
}
.font(.largeTitle)
dynamicLayout{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加- 最大と最小を具体的に指定
.frame(minWidth: 100,maxWidth: 300,minHeight: 100,maxHeight: 300)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加-最大と最小を画面サイズに合わせて可変に指定
.frame(minWidth: 0,maxWidth: .infinity,minHeight: 0,maxHeight: .infinity)
}
}
}
}
struct Essentials21LinelimitedView: View {
var body: some View {
HStack{
Image(systemName: "car")
.foregroundColor(.yellow)
.font(.largeTitle)
VStack{
Text("有限会社JULK")
.foregroundStyle(Color.green)
Text("自動車運転者をこよなく愛する会")
.font(.caption)
.foregroundStyle(Color.blue)
.layoutPriority(1)
}
.font(.largeTitle)
}
}
}
//7節で追加
struct Essentials21FramingView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottom)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomLeading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomTrailing)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .center)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerLastTextBaseline)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingLastTextBaseline)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .top)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topLeading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topTrailing)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailing)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingLastTextBaseline)
.border(Color.black)
}
}
}
}
struct Essentials21GeometryReadingView: View {
var body: some View {
GeometryReader{ geometry in
VStack{
HStack{
Image(systemName: "apple.logo")
.foregroundColor(.yellow)
.font(.largeTitle)
Text("Apple")
.foregroundStyle(Color.green)
}
.font(.largeTitle)
.frame(
width: geometry.size.width/2,
height:(geometry.size.height/4)*3
)
HStack{
Image(systemName: "apple.logo")
.foregroundColor(.red)
.font(.largeTitle)
Text("Apple")
.foregroundStyle(Color.black)
}
.font(.largeTitle)
.frame(
width: geometry.size.width/3,
height:(geometry.size.height/4)
)
}
}
}
}
"""
//タイトル
let essentialsChapter21NavigationTitle = "第21章"
let essentialsChapter21Title = "第21章 SwiftUI のスタックとフレーム"
let essentialsChapter21SubTitle = "第1節 SwiftUI のスタックとフレーム"
//ポイント
let pointEssentials21 = """
SwiftUIのスタックとしては、、、
・VStack:縦(VerticalのV)
・HStack:横(HorizontalのH)
・ZStack:重層的に重ねる
がある👀
"""
//URL
let urlEssentials21 = "https://note.com/m_kakudo/n/n0254077a8aa7"
struct Essentials21Contents: View {
var body: some View {
ScrollView{
VStack{
Essentials21OutlineView()
Essentials21ComplexityView()
Essentials21GroupedView()
Essentials21DynamicallyView()
Essentials21LinelimitedView()
Essentials21FramingView()
Essentials21GeometryReadingView()
}
}
//7節で追加
.ignoresSafeArea()
}
}
#Preview {
Essentials21Contents()
}
struct Essentials21OutlineView: View {
var body: some View {
HStack {
Image(systemName: "arrowshape.right")
}
VStack{
HStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
VStack{
Image(systemName: "arrowshape.turn.up.left.circle")
Image(systemName: "arrowshape.turn.up.left.circle.fill")
Image(systemName: "arrowshape.turn.up.right.circle")
Image(systemName: "arrowshape.turn.up.right.circle.fill")
}
}
}
}
struct Essentials21ComplexityView: View {
var body: some View {
VStack{
HStack{
Text("お会計総額")
Text("¥1050")
}
.font(.title)
.padding(15)
Spacer()
HStack(alignment: .center){
//2節で追加
Spacer()
VStack(alignment: .listRowSeparatorLeading){
Text("")
.font(.headline)
Text("Apple")
.font(.headline)
Text("Banana")
.font(.headline)
Text("Orange")
.font(.headline)
}
.padding(5)
//2節で追加
Spacer()
VStack{
Text("Menu")
Text("🍎")
Text("🍌")
Text("🟠")
}
.padding(5)
//2節で追加
Spacer()
VStack{
Text("単価")
Text("¥300")
Text("¥150")
Text("¥100")
}
//2節で追加
Spacer()
VStack{
Text("個数")
Text("2")
Text("1")
Text("3")
}
//2節で追加
Spacer()
VStack{
Text("小計")
Text("¥600")
Text("¥150")
Text("¥300")
}
//2節で追加
Spacer()
}
}
}
}
struct Essentials21GroupedView:View {
var body: some View {
VStack{
Text("和暦 月名")
VStack{
Group{
Text("睦月")
Text("如月")
Text("弥生")
Text("卯月")
Text("皐月")
Text("水無月")
Text("文月")
Text("葉月")
Text("長月")
Text("神無月")
}
Group{
Text("霜月")
Text("師走")
}
}
}
}
}
struct Essentials21DynamicallyView : View {
@State var dynamicLayout: AnyLayout = AnyLayout(VStackLayout())
var body: some View {
VStack{
HStack{
Button(action:{
dynamicLayout = AnyLayout(HStackLayout())}){
Text("横向き")
}
Button(action:{
dynamicLayout = AnyLayout(VStackLayout())}){
Text("縦向き")
}
}
.font(.largeTitle)
dynamicLayout{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加- 最大と最小を具体的に指定
.frame(minWidth: 100,maxWidth: 300,minHeight: 100,maxHeight: 300)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
//7節で追加-最大と最小を画面サイズに合わせて可変に指定
.frame(minWidth: 0,maxWidth: .infinity,minHeight: 0,maxHeight: .infinity)
}
}
}
}
struct Essentials21LinelimitedView: View {
var body: some View {
HStack{
Image(systemName: "car")
.foregroundColor(.yellow)
.font(.largeTitle)
VStack{
Text("有限会社JULK")
.foregroundStyle(Color.green)
Text("自動車運転者をこよなく愛する会")
.font(.caption)
.foregroundStyle(Color.blue)
.layoutPriority(1)
}
.font(.largeTitle)
}
}
}
//7節で追加
struct Essentials21FramingView: View {
var body: some View {
VStack{
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottom)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomLeading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .bottomTrailing)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .center)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .centerLastTextBaseline)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .leadingLastTextBaseline)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .top)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topLeading)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .topTrailing)
.border(Color.black)
}
HStack{
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailing)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingFirstTextBaseline)
.border(Color.black)
Image(systemName: "apple.logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100,alignment: .trailingLastTextBaseline)
.border(Color.black)
}
}
}
}
struct Essentials21GeometryReadingView: View {
var body: some View {
GeometryReader{ geometry in
VStack{
HStack{
Image(systemName: "apple.logo")
.foregroundColor(.yellow)
.font(.largeTitle)
Text("Apple")
.foregroundStyle(Color.green)
}
.font(.largeTitle)
.frame(
width: geometry.size.width/2,
height:(geometry.size.height/4)*3
)
HStack{
Image(systemName: "apple.logo")
.foregroundColor(.red)
.font(.largeTitle)
Text("Apple")
.foregroundStyle(Color.black)
}
.font(.largeTitle)
.frame(
width: geometry.size.width/3,
height:(geometry.size.height/4)
)
}
}
}
}
以上。
さてと、
来週からRPAの新しい現場だから、今からゆっくり休もう🕺
👉デジタルデトックスデーに入りまする
カセット垂れ流しでノーマン読んで、ぐっすり眠って、
🗑️明日は朝から公園のゴミ拾い🗑️
ま、ZStackは公式ドキュメントも載せてるし、自分で遊んでみてね〜〜〜
みなさんも良き週末を〜〜
💃では、また次回🕺