[Swift] Color拡張

ContentView.swift

extension Color {
    static var hagiiro: Color {
        return Color(red: 223/255, green: 87/255, blue: 143/255, opacity: 1.0)
    }
}

struct ContentView: View {

    var body: some View {
        ZStack{
            Circle()
                .foregroundColor(.hagiiro)
                .frame(width: 200, height:200)
            Text("萩色")
                .foregroundColor(.white)
                .font(.title)
        }
    }
}

そりゃできるよね。

[Swift] クラス定義

クラスはオブジェクトの仕様を定義したもの
L どんな属性と機能を持ち合わせたオブジェクトを作るのかを定義した設計書
L クラス定義に基づいて作ったオブジェクトをインスタンスと言う

classの書き方

class className {
	var variable:type = initial

	init(引数:type) {
		 self.variable = 引数
	}

	function fun(引数:type) -> void {
	 	return x
	}
}

playground

import UIKit

var greeting = "Hello, playground"

class MyFriend {
    var name:String
    var age:Int
    
    init(name:String, age:Int){
        self.name = name
        self.age = age
    }
    
    func hello() -> String {
        let message = "Hello! \(name)です。\(age)歳です。"
        return message
    }
}

let friend1 = MyFriend(name: "植木", age: 31)
let friend2 = MyFriend(name: "桜", age: 26)

let str1 = friend1.name + "と" + friend2.name + "は友達です。"
let str2 = friend1.name + "は" + String(friend1.age) + "歳です。"
friend2.age += 1
let str3 = friend2.name + "は誕生日で" + String(friend2.age) + "歳になりました。"

print(str1)
print(str2)
print(str3)

### 継承

class GoodFriend: MyFriend {
    let fortune = ["大吉", "吉", "小吉", "凶"]
    
    func uranai() -> String {
        let index = Int.random(in: 0 ..<fortune.count)
        let result = "今日の運勢は" + fortune[index] + "です!"
        return result
    }
    
    func who() -> String {
        return name + "です。よろしく!"
    }
}

### スーパークラス初期化

    var nickname:String
    init(name: String, age: Int, nickname: String){
        self.nickname = nickname
        super.init(name: name, age:age)
    }

### extensionを使ってクラス拡張

class Player {
    var name: String = ""
    func hello() {
        print("やあ!" + name)
    }
}

extension Player {
    var who: String {
        get {
            return name
        }
        set(value){
            name = value
        }
    }
    func bye(){
        print("またね!" + name)
    }
}

let user = Player()
user.who = "賢治"
user.hello()
user.bye()

これは数重ねるだけだなー

[Swift] どこかれでも共有できる@EnvironmentObject

クラス、メンバー、プロトコル、継承、クラス拡張などを知る

ShareData.swift

class ShareData: ObservableObject {
    @Published var isOn = false
    @Published var num = 1
}

SettingView.swift

struct SettingView: View {
    
    @EnvironmentObject var setData: ShareData
    @Binding var isPresented: Bool
    
    var body: some View {
        NavigationView {
            VStack {
                Toggle(isOn: $setData.isOn){
                    Text("設定: \(setData.isOn ? "ON": "OFF")")
                }.frame(width: 250)
                Stepper(value: $setData.num, in: 1...5){
                    Text("★ :\(setData.num)")
                }
                .frame(width: 250)
                .font(.title2)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(Color(red: 0.9, green: 0.9, blue: 0.5))
                .toolbar {
                    ToolbarItem(placement: .navigationBarTrailing){
                        Button("閉じる"){
                            isPresented = false
                        }
                    }
                }
            }
        }
    }
}

struct SettingView_Previews: PreviewProvider {
    static var previews: some View {
        SettingView(isPresented: Binding.constant(false))
            .environmentObject(ShareData())
    }
}

ContentView.swift

struct ContentView: View {
    @EnvironmentObject var setData: ShareData
    @State var isShow: Bool = false

    var body: some View {
        VStack{
            GroupBox(label: Label("設定", systemImage: "gearshape")){
                Text("\(setData.isOn ? "ON" : "OFF")")
                if setData.isOn {
                    Text(String(repeating: "★", count: setData.num))
                }
            }.frame(width: 300)
            Button (action: {
                isShow = true
            }) {
                Label("設定を変える", systemImage: "ellipsis.circle")
            }
            .padding()
            .sheet(isPresented: $isShow){
                SettingView(isPresented: $isShow)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(ShareData())
    }
}
@main
struct LinkURLSampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(ShareData())
        }
    }
}

なんかよくわからんくなってきた。
かなり修行が必要やな

[Swift] @StateObjectと@ObservedObject

@StateObjectを利用することで親ビューの再描画でオブジェクトが初期化されなくなる

ContentView.swift

struct ContentView: View {
    @State var isShow = true

    var body: some View {
        VStack (alignment: .leading, spacing: 20){
            ValueView1()
            ValueView2()
            
            Toggle(isOn: $isShow){
                
            }.frame(width: 50).padding(.top, 30)
            if isShow {
                Text("Hello, World!").font(.largeTitle)
            }
            Spacer()
        }
        .padding()
    }
}

struct ValueView2: View {
    @StateObject var maker = ValueMaker()
    
    var body: some View {
        VStack (alignment: .leading, spacing: 10){
            Text("\(maker.value)")
                .font(.title)
                .foregroundColor((maker.value > 0.8) ? .white : .gray)
                .background((maker.value > 0.8) ? Color.red : Color.white)
            HStack {
                Text("カウンタ:")
                Text("\(maker.counter)").font(.largeTitle)
            }
        }
        .background(Color.blue.opacity(0.3))
        .frame(width: 200, height: 80)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
        ValueView1()
        ValueView2()
    }
}

ValueMarker.swift

class ValueMaker: ObservableObject {
    
    @Published var value: Double
    @Published var counter: Int = 0
    private var timer: Timer
    
    init() {
        value = 0.0
        timer = Timer()
        start()
    }
    
    func start(){
        timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) {
            _ in
            self.value = Double.random(in: 0 ... 1)
            if self.value > 0.8 {
                self.counter += 1
            }
        }
    }
}

struct ValueView1: View {
    @ObservedObject var maker = ValueMaker()
    
    var body: some View {
        VStack (alignment: .leading, spacing: 10){
            Text("\(maker.value)")
                .font(.title)
                .foregroundColor((maker.value > 0.8) ? .white : .gray)
                .background((maker.value > 0.8) ? Color.red : Color.white)
            HStack {
                Text("カウンタ:")
                Text("\(maker.counter)").font(.largeTitle)
            }
        }
        .background(Color.yellow.opacity(0.3))
        .frame(width: 200, height: 80)
    }
}

なんかうまくいかんなー

[Swift] リストのスワイプアクションを処理

BookData.swift

import Foundation

struct Book: Identifiable, Equatable {
    var id = UUID()
    var title:String
    var author:String
    var isRead = false
}

class Books: ObservableObject {
    @Published var booksData = [
        Book(title: "風の又三郎", author: "宮沢賢治"),
        Book(title: "人間失格", author: "太宰治"),
        Book(title: "坊ちゃん", author: "夏目漱石"),
        Book(title: "遠野物語", author: "柳田國男"),
        Book(title: "生まれいずる悩み", author: "有島武郎"),
        Book(title: "舞姫", author: "森鴎外"),
        Book(title: "人間椅子", author: "江戸川乱歩"),
        Book(title: "人間レコード", author: "夢野久作"),
        Book(title: "山月記", author: "中島敦")
    ]
    
    func toggleIsRead(_ item: Book){
        guard let index = booksData.firstIndex(of: item) else { return }
        booksData[index].isRead.toggle()
    }
}

ContentView.swift

struct ContentView: View {
    @ObservedObject var books = Books()

    var body: some View {
        List(books.booksData) {
            item in
            BookView(item)
                .swipeActions(edge: .leading){
                    Button {
                        books.toggleIsRead(item)
                    } label: {
                        if item.isRead {
                            Label("未読にする", systemImage: "book.closed")
                        } else {
                            Label("既読にする", systemImage: "book.fill")
                        }
                    }.tint(.blue)
                }
        }.listStyle(.plain)
    }
}

@ViewBuilder
func BookView(_ item:Book) -> some View {
    VStack(alignment: .leading){
        Text(item.title).bold()
        Text(item.author)
    }
    .foregroundColor(item.isRead ? .gray : .black)
    .frame(height: 80)
}

なるほどー

[Swift] 刻々と変わるオブジェクトの値を表示

ValueMaker.swift

import Foundation

class ValueMaker: ObservableObject {
    
    @Published var value: Double = 0.0
    private var timer = Timer()
    
    func start() {
        timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) {
            _ in
            self.value = Double.random(in: 0 ... 1)
        }
    }
}

ContentView.swift

struct ContentView: View {
    @ObservedObject var maker = ValueMaker()

    var body: some View {
        VStack (alignment: .leading, spacing: 10){
            Text("\(maker.value)")
                .font(.largeTitle)
                .foregroundColor((maker.value > 0.8) ? .white : .gray)
                .background((maker.value > 0.8) ? Color.red : Color.white)
            ZStack {
                Rectangle().stroke(.gray)
                Rectangle()
                    .foregroundColor(.green)
                    .scaleEffect(x:maker.value, y:1.0, anchor: .leading)
            }
        }
        .frame(width: 200, height: 80)
        .onAppear(perform: {
            maker.start()
        })
    }
}

Timer.scheduledTimerとして、Double.random(in: 0 … 1)で値を出力する
@ObservedObject var maker = ValueMaker() でValueMakerを呼び出している

[Swift] オブジェクトを見張ってビュー表示を更新

ObservableObjectプロトコル、@Published、@ObservedObjectを利用する

### クラス定義とプロパティを使った処理

struct ContentView: View {
    @ObservedObject var user = User()

    var body: some View {
        VStack(alignment:.leading, spacing:15){
            Group {
                TextField("名前", text: $user.name)
                TextField("身長", text: $user.tall)
                    .keyboardType(.numberPad)
            }.textFieldStyle(RoundedBorderTextFieldStyle())
            if !(user.name).isEmpty && !(user.tall).isEmpty {
                Text("\(user.name)さんは\(fitSize(tall: user.tall))")
            }
        }.frame(width: 250)
    }
}

class User: ObservableObject {
    @Published var name = ""
    @Published var tall = ""
}

func fitSize(tall:String) -> String {
    guard let height = Double(tall) else {
        return "???"
    }
    
    switch height {
    case 145..<155 :
        return "Sサイズです"
    case 155..<176 :
        return "Mサイズです"
    case 176..<185 :
        return "Lサイズです"
    default:
        return "適したサイズがありません"
    }
}

面白い
名前と身長をclassにする

[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の機能か、勉強になるね。