マージ済みのローカルブランチを一括削除する

モチベーション

マージ時にリモートブランチを自動で削除するにはGitHubのSettingsの「Automatically delete head branches 」をオンにすれば良いが、ローカルブランチは放置になりがちのため解決したい。

コード

git branch --merged | xargs -n 1 | egrep -v "develop|\*" | xargs git branch -d

参考

SwiftUIアニメーション練習: rotationEffectとscaleEffect

開発環境

> xcodebuild -version 
Xcode 13.1
Build version 13A1030d

記事中のスクリーンショットのOS: iOS15

モチベーション

(シャニマス4周年WEB CM第3弾~イルミネーションスターズ・アルストロメリア・シーズ篇~【アイドルマスター】 - YouTube より)

このアニメーションSwiftUIでどうやるんだ? 書こう!

つくったもの

コード

import SwiftUI

struct ContentView: View {
    @State private var flag = false
    private let customRed = Color.init(red: 255/255, green: 186/255, blue: 214/255)
    private let customBlue = Color.init(red: 20/255, green: 67/255, blue: 132/255)
    private let customYellow = Color.init(red: 255/255, green: 224/255, blue: 18/255)
    
    private let width: CGFloat = 320
    private let height: CGFloat = 180
    
    var body: some View {
        ZStack {
            // ■□□: 赤(下から上)
            VStack {
                Spacer()
                HStack(spacing: 0) {
                    Rectangle()
                        .fill(customRed)
                        .frame(width: width/3, height: height)
                        .offset(y: flag ? 0 : height)
                        .animation(.easeIn(duration: 0.475), value: flag)
                    Rectangle()
                        .fill(.clear)
                        .frame(width: width/3, height: height)
                    Rectangle()
                        .fill(.clear)
                        .frame(width: width/3, height: height)
                }
                Spacer()
            }
            // □□■: 黄(上から下)
            VStack {
                Spacer()
                HStack(spacing: 0) {
                    Rectangle()
                        .fill(.clear)
                        .frame(width: width/3, height: height)
                    Rectangle()
                        .fill(.clear)
                        .frame(width: width/3, height: height)
                    Rectangle()
                        .fill(customYellow)
                        .frame(width: width/3, height: height)
                        .offset(y: flag ? 0 : -height)
                        .animation(.easeIn(duration: 0.475), value: flag)
                    
                }
                Spacer()
            }
            // □■□: 青(中央)
            VStack {
                Spacer()
                Rectangle()
                    .fill(customBlue)
                    .frame(width: width, height: height)
                    .scaleEffect(flag ? CGSize(width: 9.0/16.0, height: 16.0/(9.0*3.0)) : CGSize(width: 1, height: 1), anchor: .center)
                    .rotationEffect(Angle.degrees(flag ? 90 : 0))
                    .animation(.easeIn(duration: 0.5), value: flag)
                Spacer()
            }
            // 背景
            VStack(spacing: 0) {
                Rectangle()
                    .fill(.black)
                Rectangle()
                    .fill(.clear)
                    .frame(width: width, height: height)
                Rectangle()
                    .fill(.black)
            }
            // ボタン
            VStack {
                Spacer()
                Button("toggle") {
                    flag.toggle()
                }
            }
        }
    }
}

ハマったところ

scaleEffect(_:anchor:) に渡すのは比率

scaleEffect(_:anchor:) に渡すのは比率のため、比率を計算する必要がある。

// □■□: 青(全体から中央)
VStack {
    Rectangle()
        ...
        .scaleEffect(flag ? CGSize(width: 9.0/16.0, height: 16.0/(9.0*3.0)) : CGSize(width: 1, height: 1), anchor: .center)
        ...
}

rotationEffect、scaleEffectの定義順

(定義順というより宣言順?)

rotationEffectとscaleEffectを一つのViewに適用させたい場合、定義順で評価順が変わるため比率の計算をする場合に注意。

rotationEffectをA, scaleEffectをBとすると、

A -> B B -> A
import SwiftUI

struct ContentView: View {
    @State private var flag = false
    
    var body: some View {
        VStack {
            Spacer()
            Rectangle()
                .fill(.blue)
                .frame(width: 50, height: 50)
            
                // A
                .rotationEffect(Angle.degrees(flag ? 90 : 0))
                
                
                // B
                .scaleEffect(flag ? CGSize(width: 3.0, height: 1.0) : CGSize(width: 1, height: 1), anchor: .center)

                .animation(.easeIn(duration: 0.5), value: flag)
            Spacer()
            Button("toggle") {
                flag.toggle()
            }
        }
    }
}

参考

GraphQLのfragmentを試す

ミリしらだけどAndroidにもfragmentってあるよね。


モチベーション

  • fragmentを使ったことがないので使ってみたい

fragment

graphql.org

graphql.orgのfragmentの説明がいきなり例から入ってちょっと笑った。たしかに例交えたほうが説明しやすい。

fragmentを使うことで以下のことが実現できる。

  • フィールドの集合を構成できる
  • クエリに含めることができる

さっそく書いてみよう。

コード

GraphQLサーバーはlucasbento/graphql-pokemonを利用。
fragmentを無理やり使用するケースを考えた。

{
  "data": {
    "pokemon": {
      "number": "025",
      "name": "Pikachu",
      "types": [
        "Electric"
      ]
    },
    "pokemons": [
      {
        "number": "001",
        "name": "Bulbasaur",
        "types": [
          "Grass",
          "Poison"
        ]
      },
      {
        "number": "002",
        "name": "Ivysaur",
        "types": [
          "Grass",
          "Poison"
        ]
      }
    ]
  }
}

下記に記載するクエリはどちらも上記のレスポンスを得る。

fragmentを使用しない場合

query {
  pokemon(name: "Pikachu") {
    number
    name
    types
  }
  pokemons(first: 2) {
    number
    name
    types
  }
}

fragmentを使用した場合

query {
  pokemon(name: "Pikachu") {
     ...pokemonFragment
  }
  pokemons(first: 2) {
     ...pokemonFragment
  }
}

fragment pokemonFragment on Pokemon {
  number
  name
  types
}

通化できていて良い。
また、以下のようにfragmentとフィールドをまとめて利用することもできる。

query {
  pokemon(name: "Pikachu") {
     ...pokemonFragment
  }
  pokemons(first: 2) {
     ...pokemonFragment
     id  // ココ!
  }
}

fragment pokemonFragment on Pokemon {
  number
  name
  types
}

参考