struct ContentView: View { @State var isShow: Bool = false var body: some View { Button(action: { isShow = true }){ Text("シートを表示") } .sheet(isPresented: $isShow){ SomeView(isPresented: $isShow) } } } struct SomeView: View { @Binding var isPresented: Bool var body: some View { NavigationView { VStack { Image(systemName: "ladybug").scaleEffect(2.0) Text("Hello").font(.title2).padding() } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(red: 0.9, green: 0.9, blue: 0.8)) .toolbar { ToolbarItem(placement: .navigationBarTrailing){ Button { isPresented = false } label: { Text("閉じる") } } } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() SomeView(isPresented: Binding.constant(false)) } }
Category: Swift / SwiftUI
[Swift] バイディングとオブジェクトの共有
@Binding, @Published, @ObservedObject, @StateObject, @EnvironmentObject
変数のバインディング、オブジェクトの共有を行う
### 変数を別のビューの変数と紐づけて使う
– 紐付けて使う変数を @Binding で宣言する
struct ContentView: View { @State var isChecked_person1: Bool = false var body: some View { HStack { Text("担当者1のチェック") PersonCheckMark(isChecked: $isChecked_person1) } } } struct PersonCheckMark: View { @Binding var isChecked: Bool var body: some View { Button(action: { isChecked.toggle() }) { Image(systemName: isChecked ? "person.fill.checkmark" : "person") .foregroundColor(isChecked ? .blue : .gray) .scaleEffect(2.0) .frame(width: 40, height: 40) } } }
### チェック担当者を2人に増やす
struct ContentView: View { @State var isChecked_person1: Bool = false @State var isChecked_person2: Bool = false var body: some View { VStack { HStack { Text("担当者1のチェック") PersonCheckMark(isChecked: $isChecked_person1) } HStack { Text("担当者2のチェック") PersonCheckMark(isChecked: $isChecked_person2) } Group { if isChecked_person1 && isChecked_person2 { Text("全員チェック済み").foregroundColor(.blue) } else { Text("チェック待ち").foregroundColor(.red) } } .font(.largeTitle) .padding(.top) } } }
ほう、
[Swift] タブやスワイプでビュー切り替え
struct ContentView: View { @State var selectedTag = 1 var body: some View { TabView(selection: $selectedTag){ Text("Tab Content 1").tabItem { Text("Tab Label 1")}.tag(1) Text("Tab Content 2").tabItem { Text("Tab Label 2")}.tag(2) Text("Tab Content 3").tabItem { Text("Tab Label 3")}.tag(3) } .font(.largeTitle) } }
### タブビューのビューを個別に宣言
struct ContentView: View { @State var selectedTag = 1 var body: some View { TabView(selection: $selectedTag){ HomeTabView().tabItem { Image(systemName: "house") Text("HOME") }.tag(1) WeatherTabView().tabItem { Image(systemName: "cloud.sun") Text("お天気") }.tag(2) NewsTabView().tabItem { Image(systemName: "newspaper") Text("ニュース") }.tag(3) } } } struct HomeTabView: View { var body: some View { VStack { Image(systemName: "music.note.house") .scaleEffect(x: 3.0, y: 3.0) .frame(width: 100, height: 100) Text("HOME").font(.system(size: 20)) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(red: 0.5, green: 0.9, blue: 0.9)) .ignoresSafeArea() } } struct WeatherTabView: View { var body: some View { VStack { Image(systemName: "cloud.sun") .scaleEffect(x: 3.0, y: 3.0) .frame(width: 100, height: 100) Text("お天気ページ").font(.system(size: 20)) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(red: 1.0, green: 0.9, blue: 1.0)) .ignoresSafeArea() } } struct NewsTabView: View { var body: some View { VStack { Image(systemName: "newspaper") .scaleEffect(x: 3.0, y: 3.0) .frame(width: 100, height: 100) Text("ニュースと解説").font(.system(size: 20)) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(red: 0.9, green: 0.9, blue: 0.8)) .ignoresSafeArea() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() HomeTabView() WeatherTabView() NewsTabView() } }
TabViewの機能か、勉強になるね。
[Swift] グリッドレイアウト
LazyVGrid, LazyHGrid, GridItem, fixed, flexible, adaptive
struct ContentView: View { let grids = Array(repeating: GridItem(.fixed(80)), count:4) var body: some View { ScrollView() { LazyVGrid(columns: grids){ ForEach((1...100), id: \.self) { num in Page(str: String(num)) .cornerRadius(8) .frame(height: 60) } } } } }
グリッドのサイズ指定の3つのモード
– LazyVGrid(columns:grids)のグリッドレイアウトで何列並べるか、列幅はどうするかは配列gridsで決まる
GridItem(_size: GridItem.Size = .flexible(), spacing: CGFloat? = nil, alignment:Alignment? = nil)
struct ContentView: View { let grids = [GridItem(.fixed(30), spacing: 10, alignment: .center), GridItem(.fixed(50), spacing: 10), GridItem(.fixed(240))] var body: some View { LazyVGrid(columns: grids, alignment: .leading, spacing: 20){ ForEach(photoArray) { item in Image(systemName: "doc") Text(item.imageName).font(.caption) Text(item.title) } }.padding() } } struct PhotoData: Identifiable { var id = UUID() var imageName:String var title:String } var photoArray = [ PhotoData(imageName: "IMG_0463", title: "台風で流された親柱"), PhotoData(imageName: "IMG_0495", title: "横須賀ヴェルニー記念講演"), PhotoData(imageName: "IMG_1378", title: "恋人たちの湘南平テレビ塔"), PhotoData(imageName: "IMG_1739", title: "赤い漁具倉庫1"), PhotoData(imageName: "IMG_1742", title: "赤い漁具倉庫2"), PhotoData(imageName: "IMG_2233", title: "江ノ電501系"), PhotoData(imageName: "IMG_2406", title: "茅ヶ崎漁港引き上げモーター小屋"), PhotoData(imageName: "IMG_2407", title: "茅ヶ崎漁港第二えぼし丸"), PhotoData(imageName: "IMG_2864", title: "相模川河口調整水門"), PhotoData(imageName: "IMG_2909", title: "つくばエキスポセンター H2ロケット") ]
struct ContentView: View { let grids = [GridItem(.fixed(150), spacing: 20, alignment: .leading), GridItem(.fixed(20), spacing: 5, alignment: .leading), GridItem(.fixed(20), alignment: .leading)] var body: some View { ScrollView(.horizontal){ LazyHGrid(rows: grids, spacing:20){ ForEach(photoArray) { item in Image(item.imageName) .resizable() .aspectRatio(contentMode: .fit) .cornerRadius(8) Text(item.imageName).bold() Text(item.title).font(.caption) } }.padding() } } }
struct ContentView: View { let grids = [ GridItem(.adaptive(minimum: 80, maximum:.infinity)) ] var body: some View { ScrollView{ LazyVGrid(columns: grids, alignment: .leading, spacing: 10){ ForEach(1...100, id:\.self){ num in Ball(str: String(num)) .frame(width: 50, height: 50) } .padding() } } } } struct Ball: View { let str:String var body: some View { ZStack { Circle() .fill(Color.red) Text(str) .font(.title) .foregroundColor(.white) } } }
うおおおおおおお、なんか凄いことになってるな…
[Swift] 複数のビューをスクロール表示
ContentView.swift
struct ContentView: View { var body: some View { ScrollView { LazyVStack{ ForEach(0..<10) { num in Page(str: String(num)) .frame(width:200, height: 150) .cornerRadius(8) } } } .frame(width: 250, height: 500) .background(Color.gray.opacity(0.2)) } } struct Page: View { let colors:[Color] = [.green, .blue, .pink, .orange, .purple] let str:String var body: some View { ZStack { colors.randomElement() Text(str) .font(.largeTitle) .foregroundColor(.white) } } }
横スクロール
struct ContentView: View { let w:CGFloat = UIScreen.main.bounds.width-20 var body: some View { VStack(alignment: .leading){ Text("横スクロール").padding([.leading]) ScrollView(.horizontal){ LazyHStack(alignment: .center, spacing: 10){ ForEach(0..<10) { num in Page(str: String(num)) .frame(width: w, height: 150) .cornerRadius(8) } } } .frame(height: 200) .background(Color.gray.opacity(0.2)) } } }
LazyVStackとLazyHStackで見えてるところをスクロールさせるのね
なるほど、少しずつキャズムに足を踏み入れることが出来る様になってきた…
[Swift] スクロールビュー
ScrollView, LazyVStack, LazyHStack, UIScreen.main.bounds, ForEach-in
PhotoData.swift
import Foundation struct PhotoData: Identifiable { var id = UUID() var imageName:String var title:String } var photoArray = [ PhotoData(imageName: "IMG_0463", title: "台風で流された親柱"), PhotoData(imageName: "IMG_0495", title: "横須賀ヴェルニー記念講演"), PhotoData(imageName: "IMG_1378", title: "恋人たちの湘南平テレビ塔"), PhotoData(imageName: "IMG_1739", title: "赤い漁具倉庫1"), PhotoData(imageName: "IMG_1742", title: "赤い漁具倉庫2"), PhotoData(imageName: "IMG_2233", title: "江ノ電501系"), PhotoData(imageName: "IMG_2406", title: "茅ヶ崎漁港引き上げモーター小屋"), PhotoData(imageName: "IMG_2407", title: "茅ヶ崎漁港第二えぼし丸"), PhotoData(imageName: "IMG_2864", title: "相模川河口調整水門"), PhotoData(imageName: "IMG_2909", title: "つくばエキスポセンター H2ロケット") ]
PhotoView.swift
import SwiftUI struct PhotoView: View { var photo:PhotoData var body: some View { VStack { Image(photo.imageName) .resizable() .aspectRatio(contentMode: .fit) Text(photo.title) .bold() .padding(.top, 10) .padding(.bottom, 20) } .background(Color(red: 0.3, green: 0.8, blue: 0.5)) .cornerRadius(8) } } struct PhotoView_Previews: PreviewProvider { static var previews: some View { PhotoView(photo:photoArray[0]) } }
写真データを取り込んでスクロール表示する
struct ContentView: View { var body: some View { ScrollView { LazyVStack(alignment: .center, spacing: 20){ ForEach(photoArray) { photoData in PhotoView(photo:photoData) } } }.padding(.horizontal) } }
うおおおおおおおおお
これは凄いな
[Swift] ハーフモーダルビュー
struct ContentView: View { @State var isModal: Bool = false var body: some View { Button(action: { isModal = true }) { Text("Sheet テスト") } .sheet(isPresented: $isModal){ SomeView() } } }
struct ContentView: View { @State var isModal: Bool = false @State var counter:Int = 0 var body: some View { VStack { Button(action: { isModal = true }) { Text("Sheetテスト") } .sheet(isPresented: $isModal, onDismiss: {countUp(}){ SomeView() } .disabled(counter >= 3) Text("回数\(counter)") .font(.title) .paddin() } } func countUp(){ counter += 1 } }
なんか久しぶりにSwift触ると、全然頭がついていけないな
少しずつでも毎日継続してやらないと駄目だ
[Swift] URLSessionによるPOST
Button("Place Order"){ Task { await placeOrder() } } func placeOrder() async { guard let encoded = try? JSONEncoder().encode(order) else { print("Failed to encode order") return } let url = URL(string: "https://hoge.com")! var request = URLRequest(url: url) request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" do { let (data, _) = try await URLSession.shared.upload(for: request, from: encoded) } catch { print("Checkout failed.") } }
どのようにデータをPOSTしているのか、なんとなくイメージはついた。
ああああ、Swiftやらなきゃあああああああああああああ
どうしよう、これ。。。
[SwiftUI] 確認ダイアログの表示
struct ContentView: View { @State private var isShowingDialog = false var body: some View { Button(action: { isShowingDialog = true }) { Label("削除ボタン", systemImage: "trash") }.confirmationDialog("注意!", isPresented: $isShowingDialog) { Button("削除する", role: .destructive){ destructiveAction() } Button("キャンセル", role: .cancel){ cancelAction() } } message: { Text("削除すると戻せません") } } func destructiveAction(){ print("削除が選ばれた") } func cancelAction(){ print("キャンセルが選ばれた") } }
### 確認ダイアログのタイトル表示
var body: some View { Button(action: { isShowingDialog = true }) { Label("削除ボタン", systemImage: "trash") }.confirmationDialog("注意!", isPresented: $isShowingDialog, titleVisibility: .visible) { Button("選択A"){ } Button("選択B"){ } Button("削除する", role: .destructive){ destructiveAction() } Button("キャンセル", role: .cancel){ cancelAction() } } message: { Text("メッセージ、メッセージ、メッセージ、メッセージ") + Text("メッセージ、メッセージ、メッセージ、メッセージ") } }
buttonのactionの箇所は何も記載がないが、ここに実行文を書くのね。
[SwiftUI] アラート文
struct ContentView: View { @State var isError: Bool = false var body: some View { Button(action:{ isError = true }){ Text("Alertテスト") }.alert(isPresented: $isError){ Alert(title: Text("タイトル"), message: Text("メッセージ文"), dismissButton: .default(Text("OK"), action: {})) } } }
OKとキャンセルがあるアラート
struct ContentView: View { @State var isError: Bool = false var body: some View { Button(action:{ isError = true }){ Text("Alertテスト") }.alert(isPresented: $isError){ Alert(title: Text("タイトル"), message: Text("メッセージ文"), primaryButton: .default(Text("OK"), action: { okAction() }), secondaryButton: .cancel(Text("キャンセル"), action:{}) )} } } func okAction(){ print("OKボタンが選ばれた") }
primaryButtonとsecondaryButtonを指定する
struct ContentView: View { @State var isError: Bool = false var body: some View { Button(action:{ isError = true }){ Text("Alertテスト") }.alert(isPresented: $isError){ Alert(title: Text("タイトル"), message: Text("メッセージ文"), primaryButton: .destructive(Text("削除する"), action: {}), secondaryButton: .cancel(Text("キャンセル"), action:{}) )} } }
やべーな、swiftかなり舐めてたわ…orz