[Swift] @Bindingを使ってビューを閉じるボタンを作る

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))
    }
}

[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触ると、全然頭がついていけないな
少しずつでも毎日継続してやらないと駄目だ

[Linux] evalとは

文字列を評価、連結して実行する

hoge.txt

hoge hoge

fuga.txt

fuga fuga fuga

example.sh

#!/bin/bash
value='hoge'

#文字列としてコマンドを変数に格納
cmd='grep $value hoge.txt'
echo $cmd
eval $cmd

$ sh example.sh
grep $value hoge.txt
hoge hoge

#!/bin/bash

grep_text() {
	for txt_file in $(ls . | grep ".txt$"); do
		grep_result=$(grep $1 $txt_file)
		if [ $? -eq 0 ]; then
			eval echo $2
		fi
	done
}

query='hoge'
message='検索対象が見つかりました。見つかったファイル名:$txt_file'
grep_text $query "${message}"

query='fuga'
message='検索対象が見つかりました。見つかったファイル名:$grep_result'
grep_text $query "${message}"

$ sh success.sh
検索対象が見つかりました。見つかったファイル名:hoge.txt
検索対象が見つかりました。見つかったファイル名:fuga fuga fuga

何これ、やればやるほど次から次へと課題が出てくる

sedコマンド

文字列を全置換したり行単位で抽出したり、削除したり、テキスト処理できるコマンド
コマンドラインパラメータで指定して非対話的に一括処理もできる
sedはStream EDitorの略

### sedの構文
sed OPTIONS… [SCRIPT] [INPUTFILE…]
[SCRIPT]とは “s/foo/bar/g”
“-e”オプションで直後に[SCRIPT]が来る

$ echo “Tech Blog” | sed -e “s/Blog/Comment/g”
Tech Comment
$ echo “Tech Blog” | sed -e “s/ /-/g”
Tech-Blog

バックスラッシュはエスケープ
$ echo “Tech Blog” | sed -e “s/ /\!/”
Tech!Blog

二つ目に見つかった”o”を”_”に変換
$ echo “Hello World” | sed -e “s/o/__/2”
Hello W__rld

### ファイルの書き換え
$ echo “Hello World” > sample.txt
$ sed -e “s/World/Coffee/g” sample.txt
Hello Coffee
$ cat sample.txt
Hello World
$ sed -i -e “s/World/Shinbashi/g” sample.txt
$ cat sample.txt
Hello Shinbashi

他にも色々使い方ができる
取り敢えず置換ができると覚えておく

GitLab CIを活用

.gitlab-ci.yml

stages:
  - npm
  - composer
  - upload

npm:
  stage: npm
  image: node:12.14.1-alpine3.111
  script:
    - npm install
    - npm audit fix
    - npm run production
    - tar czf node_modules.tar.gz node_modules 
  artifacts:
    paths:
      - node_modules.tar.gz

composer:
  stage: composer
  image: composer:1.9
  script:
    - composer install
    - zip -r ./${CI_PIPELINE_ID}.zip .
  artifacts:
    paths:
      - ./${CI_PIPELINE_ID}.zip

s3upload:
  stage: upload
  image: alpine:latest
  before_script:
    - apk add --no-cache python3
    - pip3 install awscli
  script:
    - aws s3 cp ./${CI_PIPELINE_ID}.zip s3://${S3BUCKET}/${APP}.zip
build:
  stage: build
  script:
    - echo compile and package
    - echo tag image version
    - branch_name=$(echo $CI_COMMIT_REF_NAME | sed 's/\//-/g')
    - version="$branch_name-$CI_PIPELINE_ID"
    - echo login ECR and push image
    - eval $(aws ecr get-login --no-include-email --region ap-northeast-1)
    - docker tag app:latest myimage:${version}
    - docker push myimage:${version}
  only:
    refs:
      - feature
      - develop
      - integration
      - hotfix
      - master
    changes:
      - src/*/*
  tags:
    - build-runner

deploy:
  stage: deploy
  script:
    - echo "Deploy app"
    - branch_name=$(echo $CI_COMMIT_REF_NAME | sed sed 's/\//-/g')
    - version="$branch_name-$CI_PIPELINE_ID"
    - echo $version > codedeploy/image_version.txt
    - cd codedeploy
    - zip -r deploy.zip appspec.yml image_version.txt scripts
    - aws s3 cp deploy.zip s3://codedeploy/automation/${CI_COMMIT_REF_NAME}/app/deploy.zip --metadata x-amz-meta-application-name=app,x-amz-meta-deploymentgroup-name=${obj}
  only:
    refs:
      - feature
      - develop
      - integration
      - hotfix
      - master
    changes:
      - src/**/*
  tags:
    - deploy-runner

install.sh

#!/bin/bash
# Script is run on instance

# Get app version
dir=$(dirname "$0")
version=$(cat ${dir}/../image_version.txt)

# Tracking version
OPS_DIR="/ect/ops"
export APP_VERSION=${version}

# Compose up
docker-compose up -d app

appspec.yml

version: 0.0
os: linux
hooks:
  BeforeInstall:
    - location: scripts/install.sh
      timeout: 300
      runas: root