😇

Flutter initState & setState -> SwiftUI ???

2024/07/06に公開

対象者

  • SwiftUIに興味がある人
  • 他のFWでライフサイクルを学んだ人

最近、副業でSwiftUIを使っています。FlutterやReactと同じように、宣言的UIと呼ばれ、コードを書くだけで、イメージしたUIを再現できるFWなのだなと思いつつまだまだ理解が足りないので普段から、自己学習をしています。

プロジェクトの説明

Flutter & React.js で書いたコードを SwiftUIに置き換えて、ページが呼ばれた時の処理と画面を更新するだけの処理ですが、理解しながら、覚えていこうと思います。

Flutterの場合だと、ページが呼ばれたタイミングで、initStateを実行し、一度だけ処理を実行。ボタンを押すと、setStateで画面を更新するライフサイクルが実行されます。

Flutterの場合

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomePage(),
    );
  }
}


class HomePage extends StatefulWidget {
  const HomePage({super.key});

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool isLoading = false;

  int count = 0;

  // count++
  void increment() {
    setState(() {
      count++;
    });
  }

  // 3秒カウントするログを出す
  void startCount() {
    setState(() {
      isLoading = true;
    });
    Future.delayed(const Duration(seconds: 3), () {
      setState(() {
        isLoading = false;
        count++;
      });
    });
  }

  
  void initState() {
    // 画面が表示された時に3秒カウントを開始する
    startCount();
    super.initState();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      body: Center(
        child: isLoading
            ? const CircularProgressIndicator()
            : Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('Count: $count'),
                  ElevatedButton(
                    onPressed: increment,
                    child: const Text('Start Count'),
                  ),
                ],
              ),
      ),
    );
  }
}

Reactの場合だと、 ブラウザに、ページが表示されたら、useEffectの中の処理を実行し、ボタンを押すイベントが実行されると、useStateで画面を更新します。

Reactの場合
import React, { useState, useEffect } from "react";

export default function App() {
  return (
    <div className="App">
      <HomePage />
    </div>
  );
}

function HomePage() {
  const [isLoading, setIsLoading] = useState(false);
  const [count, setCount] = useState(0);

  // count++
  const increment = () => {
    setCount((prevCount) => prevCount + 1);
  };

  // 3秒カウントする
  const startCount = () => {
    setIsLoading(true);
    setTimeout(() => {
      setIsLoading(false);
      setCount((prevCount) => prevCount + 1);
    }, 3000);
  };

  // 画面が表示された時に3秒カウントを開始する
  useEffect(() => {
    startCount();
  }, []);

  return (
    <div style={{ textAlign: "center" }}>
      <h1>Home Page</h1>
      <div>
        {isLoading ? (
          <p>Loading...</p>
        ) : (
          <div>
            <p>Count: {count}</p>
            <button onClick={increment}>Start Count</button>
          </div>
        )}
      </div>
    </div>
  );
}

SwiftUIの場合だと、ページが呼ばれたときは、.onAppearで処理を実行する。ボタンを押すイベントが起きた時に、画面を更新するときは、@Stateを使う。

SwiftUIの場合
import SwiftUI

struct ContentView: View {
    @State private var isLoading = false
    @State private var count = 0
    
    var body: some View {
        NavigationStack {
            ZStack {
                if isLoading {
                    ProgressView()
                } else {
                    VStack {
                        Text("Count: \(count)")
                        Button("Start Count") {
                            increment()
                        }
                    }
                }
            }
            .navigationTitle("Home Page")
        }
        .onAppear {
            startCount()
        }
    }
    
    func increment() {
        count += 1
    }
    
    func startCount() {
        isLoading = true
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            isLoading = false
            count += 1
        }
    }
}

感想

ざっくりとですけど、ライフサイクルは似ていることを解説しました。同じようなFWやライブラリを知っていれば、プログラミング言語のルールを覚えるだけなので、そんなには、難しくはないかもしれません。

  1. ページが呼ばれたとき
  2. 画面が更新される
  3. 状態を破棄する

setState & useState -> @State
initState & useEffect -> onAppear

https://developer.apple.com/documentation/swiftui/state
https://developer.apple.com/documentation/swiftui/view/onappear(perform:)

Discussion