見出し画像

SwiftUIでいこう! - macOS App (1)

Swift Playgrounds 4.1( MacではPlaygrounds.app )となって Xcodeを使わずMac アプリも作れるようになったので試しに参考サイトを見ながら作ってみます。

まずは基本形

struct ContentView: View {
    var body: some View {
        NavigationView{
            
            ListView()

            MainView()
            
        }
        .frame(minWidth: 600, minHeight: 400)
    }
}

struct ListView:View{
    var body: some View {
        Text("List")
        
    }
}

struct MainView:View{
    var body: some View {
        Text("Main")
        
    }
}

"NavigationView"を使っているので、こんな感じの2ペインの画面になります。

目標は

左側には選べるリスト"ListView()"。メニューのようなもの。そして選んだものを表示する所に"MainView()"というように作っていきます。        

実際にListView()とMainView()の中身を作っていきます。

ListView()にはリスト表示で右側の内容を選べるようにするための準備。まず、画像とタイトル名を使いまわすための構造体を作ります。

struct Option:Hashable{
    let title:String
    let imageName:String
}

そして作った構造体を使うために変数宣言して名前と画像名を指定します。

struct ContentView: View {
    let option:[Option] = [
        .init(title: "Home", imageName: "house"),
        .init(title: "About", imageName: "info.circle"),
        .init(title: "Setting", imageName: "wrench.fill")
    ]
}

そして

struct ListView:View{
    let options:[Option]
    var body: some View {
        VStack{
            ForEach(options, id: \.self){option in
                HStack{
                    Image(systemName: option.imageName)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width:30)
                    Text(option.title)
                    Spacer()
                }
                .padding()
            }
            Spacer()
        }
    }
}

画像、タイトルという感じで横並びにするので

HStack{
     Image(systemName: option.imageName)
     Text(option.title)
}

を設置します。これを作った変数(配列)に入っているデータを利用したいのでForEach()を使って一つずつ取り出して表示するようにします。

ForEach(options, id: \.self){option in
   // 処理
}

Space()とpaddingで位置を調整しています。

あとはMainView()を作っていくのですがとりあえず、画像を貼り付けると、

struct MainView:View{
    var body: some View {
        Image("swPic")
            .resizable()
            .aspectRatio(contentMode: .fit)
    }
}

入れたい画像名を指定して(事前画像をPlaygroundsに取り込んでおきます)

メニューのファイルからファイルの追加を選んで画像を取り込みます。
画像の指定はファイル名のみでOKです。

Image("swPic")

画像の大きさを自動で調整させています。

.resizable()
.aspectRatio(contentMode: .fit)


左のメニューの中にある項選んで必要なもの画面を表示する仕組みを作っていきます。 "ContentView"ですが、全体としては以下のようになります。

struct ContentView: View {
    @State var currentOption = 0
    
    let option:[Option] = [
        .init(title: "Home", imageName: "house"),
        .init(title: "About", imageName: "info.circle"),
        .init(title: "Setting", imageName: "wrench.fill")
    ]
    
    var body: some View {
        NavigationView{
            
            ListView(options: option,currentSelection: $currentOption)
            
            switch currentOption{
                
                case 1:
                    Text("About")
                default:
                    MainView()

            }
        }
    }
}

追加部分は

@State var currentOption = 0

修正部分は"NavigationView"の中、ListView()と表示の切り替えの記述追加

ListView(options: option, currentSelection: $currentOption)

switch currentOption{
      case 1:
          Text("About")
      default:
          MainView()
}

"switch"で条件により表示を変えます。

この"switch"の条件となる変数はListView()からの引き継ぎによるものなのでListView()では変数を"@Binding"と変数宣言します。

@Binding var currentSelection:Int

あとは文字を選択した時にその有効なものに色をつけていくのと、文字をタップしたときの切り替えについての命令を書きます。

Text(option.title)
       .foregroundColor(current == option ? .blue : .white)
.onTapGesture {
      if currentSelection == 1{
          currentSelection = 0
      }else{
          self.currentSelection = 1
      }

ListView()の全体は以下となっています。

struct ListView:View{
    let options:[Option]
    @Binding var currentSelection:Int
    
    var body: some View {
        
        VStack{
            
            let current = options[currentSelection]
            ForEach(options, id: \.self){option in
                HStack{
                    Image(systemName: option.imageName)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width:30)
                    Text(option.title)
                        .foregroundColor(current == option ? .blue : .white)
                    Spacer()
                }
                .padding()
                .onTapGesture {
                    if currentSelection == 1{
                        currentSelection = 0
                    }else{
                        self.currentSelection = 1
                    }   
                }
            }
            Spacer()
        } 
    }
}


この記事が気に入ったらサポートをしてみませんか?