0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Swift初心者が年末年始を利用してSwiftUI Tutorialsをやってみた

Last updated at Posted at 2020-01-01

はじめに

今更感はありますが、年末年始で時間ができたので以前からずっと気になっていたSwiftUIを触ってみようと、Appleから提供されているTutorialsをやってみました。(英語能力があれなのでGoogle先生にお世話になりながら...)
学生時代に研究でObjective-Cで一般未公開ながらiPad向けアプリを作成したことがありましたが、Swiftはほとんど初心者だったため、Tutorialをやっているだけでも「Swiftってこんな書き方するんだ...」みたいな発見が多々ありました。
せっかくなのでSwift・SwiftUI含め詰まったことを自分用の備忘録として雑多にメモメモ。

やったやつ

Creating and Combining Views
Building Lists and Navigation
Handling User Input

structとclass

struct ContentView: View {
    var body: some View {
        Text("Turtle Rock")
            .font(.title)
            .foregroundColor(.green)
    }
}

structという文字から最初はC言語の構造体みたいな物なのかなと思いきや、関数も宣言できる。
他の言語同様classもあり、structclassの違いとしては以下の通り。

  • structは継承ができない
  • classは参照型、structは値型

PreviewProvider

Xcodeで作成中のViewをプレビューする内容を設定するやつ(雑。
File > New > File...Swift UIViewを選択してファイルを作成すると自動で作成されるstruct
previewsの中でViewの表示方法を定義できる。

// 普通にContentViewを表示
struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

ソースを直すとプレビューに即座に反映される。
プレビューからも部品のプロパティを弄れるし、ソースコード側に反映される。
まぁ、 EclipseとかIDEによっては同様な機能を持っている物はある。
面白いと思ったのはPreviewProviderでの定義方法によって表示をある程度自在にできるところ。
例えば、複数の部品をグループ化して表示する。しかもサイズを設定する。

struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            LandmarkRow(landmark: landmarkData[0])
            LandmarkRow(landmark: landmarkData[1])
        }
        .previewLayout(.fixed(width: 300, height: 70))
    }
}

例えば、端末別に表示をする等ができる。

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        ForEach(["iPhone SE", "iPhone XS Max", "iPad Pro (12.9-inch)"], id: \.self) { deviceName in
            LandmarkList()
                .previewDevice(PreviewDevice(rawValue: deviceName))
                .previewDisplayName(deviceName)
        }
    }
}

VStackとHStack

簡単にレイアウトできる。HTMLより楽。

  • VStack{} - 垂直方向にコンポーネントを並べる
  • HStack{} - 水平方向にコンポーネントを並べる

Stackは入れ子にもできる。
また、コンポーネント間の空白や余白も用意されている。

  • Spacer() - 空白作る
  • Padding() - 余白作る
  • Offset() - 位置をずらす
            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }

アプリ立ち上げ時の起動画面

アプリ立ち上げ時の起動画面(RootView)はSceneDelegate.swiftscene関数で設定する。
window.rootViewControllerにRootViewに設定したいViewをセットする。

SceneDelegate.swift
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            // UIHostingController()のrootViewに作成したViewを設定する。
            window.rootViewController 
                = UIHostingController(rootView: LandmarkList())
            self.window = window
            window.makeKeyAndVisible()
        }
    }

実際にアプリを実装する時、起動時に必要な初期処理(ファイル読み込みとか?)などはどこで書くのがベターなのかな?
画面と処理は分けた方が良さそうなので、RootViewに設定したViewの中で初期処理をやるのは何か違う気がする。
チュートリアルを進めたら出てくるのかな。

protocol

Javaとかでいうinterface的なアレ。
構造体でも使える。

Landmark.swift
// HashableとCodableとIdentifiableを実装している。
struct Landmark: Hashable, Codable, Identifiable {
// 略
}

ObservableObject

データを監視するためのオブジェクトを作成する。
ObservableObjectプロトコルを実装するclassを作成する。

変更を他クラスから参照できるよう、公開するプロパティには@Published属性を付ける。

UserData.swift
import SwiftUI
import Combine

final class UserData: ObservableObject {
    @Published var showFavoritesOnly = false
    @Published var landmarks = landmarkData
}

参照する側ではObservableObjectを@EnvironmentObject属性を付けて宣言する。

LandmarkList.swift
struct LandmarkList: View {
    // 監視するやーつ
    @EnvironmentObject var userData: UserData

    var body: some View {
        NavigationView {
            List {
                Toggle(isOn: $userData.showFavoritesOnly) {
                    Text("Favorites only")
                }

                ForEach(userData.landmarks) { landmark in
                    if !self.userData.showFavoritesOnly || landmark.isFavorite {
                        NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                            LandmarkRow(landmark: landmark)
                        }
                    }
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}

場所によって参照先の記述方法が変わっているのはなんじゃろ?

  • $userData.showFavoritesOnly - ドル記号はバインディングしていることを表している模様。値の変更を監視している。
  • ForEach(userData.landmarks) { landmark in - 下記でselfを付けてこちらにselfを付けない理由はなんだろ?
  • !self.userData.showFavoritesOnly - selfはJavaとかでいうthisのイメージ。

続く

Tutorialをやっただけですが、面白かったので多分続きます。

参考

SwiftUIチュートリアルをやってみた その1
Swift では Protocol を積極的に使おう

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?