テーマカラーを表示する

ソースコード:MyColorMemoAppSwiftUI

//

import SwiftUI

class MyColor: ObservableObject {
  @Published var color: Color = MyColorType.base.color
  
  func changeThemeColor(type: MyColorType) {
    color = type.color
  }
}

//

import SwiftUI
import RealmSwift

struct HomeView: View {
  @ObservedResults(MemoDataModel.self) var memoDataList
  @State var isShowColorSetting = false
  @EnvironmentObject var myColor: MyColor
  
  var body: some View {
    NavigationStack {
      VStack {
        memoList
        // Spacerを下に入れることで、メモ一覧を上に詰めるように表示させる
        Spacer()
      }
      .toolbarBackground(myColor.color, for: .navigationBar)
      .toolbarBackground(.visible, for: .navigationBar)
      // NavigationBarに設置するボタンなどを定義
      .toolbar {
        // 左上:テーマカラー設定
        ToolbarItem(placement: .navigationBarLeading) {
          colorSettingButton
        }
        // 右上:新規メモ作成
        ToolbarItem(placement: .navigationBarTrailing) {
          memoCreateLinkButton
        }
      }
    }
    
  }
  
  /// メモ一覧.
  var memoList: some View {
    List {
      ForEach(memoDataList, id: \.self) { memo in
        // NavigationLinkは画面遷移を行うViewです
        // タップでdestinationに記載したViewへNavigation遷移
        NavigationLink(
          // 画面遷移する際にタップしたセルのMemoDataを渡す
          destination:
            MemoDetailView(memoData: memo)
        ) {
          memoCell(memo)
        }
      }
      .onDelete(perform: delete)
    }
    .listStyle(.plain)
  }
  
  /// カラー設定ボタン.
  var colorSettingButton: some View {
    Button(
      action: {
        isShowColorSetting = true
      }, label: {
        Image(.colorSettingIcon)
      }
    )
    .confirmationDialog("テーマカラーを選択してください", isPresented: $isShowColorSetting) {
      ForEach(MyColorType.allCases, id: \.self) { type in
        Button(
          action: {
            myColor.changeThemeColor(type: type)
          }, label: {
            Text(type.title)
          }
        )
      }
      Button("キャンセル", role: .cancel) {}
    }
  }
  
  /// メモ作成ボタン.
  var memoCreateLinkButton: some View {
    // NavigationLinkはTextだけでなく画像やViewでも設定できる
    NavigationLink(
      // onAppearで生成した新しいMemoDataを渡す
      destination:
        MemoDetailView(memoData: MemoDataModel())
    ) {
      Image(systemName: "plus")
    }
  }
}

// MARK: View Components
extension HomeView {
    /// メモ一覧に表示するセル.
    func memoCell(_ memo: MemoDataModel) -> some View {
        VStack(alignment: .leading) {
            // メモのテキスト
            Text(memo.text)
                .font(.body)
            // メモの作成・編集日時
            Text(formatDate.string(from: memo.recordDate))
                .font(.footnote)
        }
    }
}

extension HomeView {
    // 表示形式を変更した日付
    var formatDate: DateFormatter {
        let formatter = DateFormatter()
        // Apple が推奨する固定フォーマット用ロケール
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(identifier: "UTC")
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
        return formatter
    }
}

// MARK: Methods
extension HomeView {
  /// リストからメモを削除する処理(Realm オブジェクトを削除).
  func delete(at offsets: IndexSet) {
    // map + firstで削除対象のオブジェクトを取得
    if let objectToDelete = offsets
        // インデックスを使って、対応するメモデータを取り出す
      .map({ memoDataList[$0] })
        // 今回は最初の1件だけを削除対象にする
      .first {
      // removeでリストからメモデータを削除
      self.$memoDataList.remove(objectToDelete)
    }
  }
}
      
#Preview {
  @Previewable @StateObject var myColor = MyColor()
    HomeView()
    .environmentObject(myColor)
}

#Preview {
  @Previewable @StateObject var myColor = MyColor()
    HomeView()
    .environmentObject(myColor)
}

//

import SwiftUI
import RealmSwift

struct MemoDetailView: View {
  @EnvironmentObject var myColor: MyColor
    /// 新規作成または編集するメモの情報.
    /// Binding($○○)で値を変更すると、自動的にRealmにも反映される
    @ObservedRealmObject var memoData: MemoDataModel
    /// TextEditorのフォーカス.
    @FocusState var isFocused: Bool
    
    var body: some View {
        TextEditor(text: $memoData.text)
            .focused($isFocused)
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    HStack {
                        Spacer()
                        keyboardCloseButton
                    }
                }
            }
            .navigationTitle(dateFormatter.string(from: memoData.recordDate))
            .toolbarBackground(myColor.color, for: .navigationBar)
            .toolbarBackground(.visible, for: .navigationBar)
            .onDisappear {
                // 新規作成されたメモかどうかを判定
                if memoData.realm == nil {
                    // テキストが空でなければ保存(空のままなら保存しない)
                    if !memoData.text.isEmpty {
                        saveData()
                    }
                } else {
                    // 既存メモの場合は、最終編集日時だけ更新
                    updateDate()
                }
            }
    }
    
    /// キーボードを閉じるボタン.
    var keyboardCloseButton: some View {
        Button(
            action: {
                isFocused = false
            }, label: {
                Text("done")
            }
        )
    }
}

extension MemoDetailView {
    /// Date型の値を年月日に表示を変換する.
    var dateFormatter: DateFormatter {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy年MM月dd日"
        return dateFormatter
    }
    
    /// Realmにメモデータを保存する.
    func saveData() {
        let realm = try! Realm()
        try! realm.write {
            memoData.recordDate = Date()
            realm.add(memoData)
        }
        print(memoData)
    }
    
    /// メモデータの日付を更新する.
    func updateDate() {
        // Bindingでない場合のRealmオブジェクトの更新
        // 本来Realmオブジェクトは安全性のため編集不可なので、
        // thawで解凍し、解凍したオブジェクトのwrite内で更新する
        let data = memoData.thaw()
        try! data?.realm?.write {
            // 解凍後のデータを使って、recordDateを更新
            data?.recordDate = Date()
        }
    }
}

#Preview {
    @Previewable @StateObject var myColor = MyColor()
    @Previewable let memo = MemoDataModel()
    MemoDetailView(memoData: MemoDataModel())
    .environmentObject(myColor)
}

//

import SwiftUI

@main
struct MyColorMemoAppSwiftUIApp: App {
  @StateObject var myColor = MyColor()
    var body: some Scene {
        WindowGroup {
            HomeView()
            .environmentObject(myColor)
        }
    }
}

//

import SwiftUI

enum MyColorType: CaseIterable {
  case base // #FFFFFF
  case orange // #F8C165
  case red // #D24141
  case blue // #4187FA
  case pink // #F064B9
  case green // #50AA41
  case purple // #965AD2
  
  var color: Color {
    switch self {
    case .base: return .white
    case .orange: return Color.rgbo(red: 248, green: 193, blue: 101, opacity: 1)
    case .red: return Color.rgbo(red: 210, green: 65, blue: 65, opacity: 1)
    case .blue: return Color.rgbo(red: 65, green: 135, blue: 250, opacity: 1)
    case .pink: return Color.rgbo(red: 240, green: 100, blue: 185, opacity: 1)
    case .green: return Color.rgbo(red: 80, green: 170, blue: 65, opacity: 1)
    case .purple: return Color.rgbo(red: 150, green: 90, blue: 210, opacity: 1)
    }
  }
  
  var title: String {
    switch self {
    case .base: return "デフォルト"
    case .orange: return "オレンジ"
    case .red: return "レッド"
    case .blue: return "ブルー"
    case .pink: return "ピンク"
    case .green: return "グリーン"
    case .purple: return "パープル"
    }
  }
  
}
extension Color {
  static func rgbo(red: Int, green: Int, blue: Int, opacity: CGFloat = 1) -> Color {
    return Color(red: CGFloat(red) / 255, green: CGFloat(green) / 255, blue: CGFloat(blue) / 255, opacity: opacity)
  }
}

  • ユーザー名:
  • 受講プラン:
  • 完了ステータス:未完了
cta_img

受講申し込みはこちらから

まずは受講用アカウントの作成からスタート。
iOSアカデミアの受講に必要な各種情報を記載した、ご案内メールをお届けします。

受講申し込み