• SwiftUIのパフォーマンスを解明

    SwiftUIでパフォーマンスのメンタルモデルを構築し、より速く、より効率的なコードを書く方法を学びましょう。パフォーマンスを妨げるよくある原因のいくつかとSwiftUIのハングやヒッチを学ぶことで、よりレスポンシブなViewを作成しましょう。

    関連する章

    リソース

    関連ビデオ

    WWDC23

    WWDC21

    Tech Talks

  • ダウンロード
    Array
    • 3:59 - DogView

      struct DogView: View {
        @Environment(\.isPlayTime) private var isPlayTime
        var dog: Dog
        var body: some View {
        	Text(dog.name)
        		.font(nameFont)
        	Text(dog.breed)
        		.font(breedFont)
        		.foregroundStyle(.secondary)
        		
        	ScalableDogImage(dog)
        	
        	DogDetailView(dog)
        	
        	LetsPlayButton()
        		.disabled(dog.isTired)
        }
       }
      }
    • 4:00 - ScalableDogImage

      struct ScalableDogImage: View {
      	@State private var scaleToFill = false
      	var dog: Dog
      	
      	var body: some View {
      		dog.image
      			.resizable()
      			.aspectRatio(
      				contentMode: scaleToFill ? .fill : .fit)
      			.frame(maxHeight: scaleToFill ? 500 : nil)
      			.padding(.vertical, 16)
      			.onTapGesture {
      				withAnimation { scaleToFill.toggle() }
      			}
      	}
      }
    • 4:01 - printChanges

      expression Self._printChanges()
    • 4:02 - ScalableDogImage + printChanges

      struct ScalableDogImage: View {
      	@State private var scaleToFill = false
      	var dog: Dog
      	
      	var body: some View {
          let _ = Self._printChanges()
      		dog.image
      			.resizable()
      			.aspectRatio(
      				contentMode: scaleToFill ? .fill : .fit)
      			.frame(maxHeight: scaleToFill ? 500 : nil)
      			.padding(.vertical, 16)
      			.onTapGesture {
      				withAnimation { scaleToFill.toggle() }
      			}
      	}
      }
    • 8:46 - ScaleableDogImage

      struct ScalableDogImage: View {
      	@State private var scaleToFill = false
      	var dog: Dog
      	
      	var body: some View {
      		dog.image
      			.resizable()
      			.aspectRatio(
      				contentMode: scaleToFill ? .fill : .fit)
      			.frame(maxHeight: scaleToFill ? 500 : nil)
      			.padding(.vertical, 16)
      			.onTapGesture {
      				withAnimation { scaleToFill.toggle() }
      			}
      	}
      }
    • 8:47 - Updated DogView

      struct DogView: View {
        @Environment(\.isPlayTime) private var isPlayTime
        var dog: Dog
        var body: some View {
        	Text(dog.name)
        		.font(nameFont)
        	Text(dog.breed)
        		.font(breedFont)
        		.foregroundStyle(.secondary)
        		
        	ScalableDogImage(dog)
        	
        	DogDetailView(dog)
        	
        	LetsPlayButton()
        		.disabled(dog.isTired)
        }
       }
      }
    • 8:48 - Final DogView

      struct DogView: View {
        @Environment(\.isPlayTime) private var isPlayTime
        var dog: Dog
        var body: some View {
        	DogHeader(name: dog.name, breed: dog.breed)
        		
        	ScalableDogImage(dog.image)
        	
        	DogDetailView(dog)
        	
        	LetsPlayButton()
        		.disabled(dog.isTired)
        }
       }
      }
    • 12:22 - DogRootView and FetchModel

      struct DogRootView: View {
      	@State private var model = FetchModel()
      	
      	var body: some View {
      		DogList(model.dogs)
      	}
      }
      
      @Observable class FetchModel {
      	var dogs: [Dog]
      	
      	init() {
      		fetchDogs()
      	}
      	
      	func fetchDogs() {
      		// Takes a long time
      	}
      }
    • 12:23 - Updated DogRootView and FetchModel

      struct DogRootView: View {
      	@State private var model = FetchModel()
      	
      	var body: some View {
      		DogList(model.dogs)
      			.task { await model.fetchDogs() }
      	}
      }
      
      @Observable class FetchModel {
      	var dogs: [Dog]
      	
      	init() {}
      	
      	func fetchDogs() async {
      		// Takes a long time
      	}
      }
    • 15:12 - List

      List {
      	ForEach(dogs) {
      		DogCell(dog: $0)
      	}
      }
    • 16:08 - List Again

      List {
      	ForEach(dogs) {
      		DogCell(dog: $0)
      	}
      }
    • 17:35 - List Fixed

      List {
      	ForEach(tennisBallDogs) { dog in
      		DogCell(dog)
      	}
      }
    • 18:25 - Sectioned List

      // Sectioned example
      struct DogsByToy: View {
      	var model: DogModel
      	var body: some View {
      		List {
      			ForEach(model.dogToys) { toy in
      				Section(toy.name) {
      					ForEach(model.dogs(toy: toy)) { dog in
      						DogCell(dog)
      					}
      				}
      			}
      		}
      	}
      }
    • 19:21 - DogTable

      struct DogTable: View {
      	var dogs: [Dog]
      	var body: some View {
      		Table(of: Dog.self) {
      			// Columns
      		} rows: {
      			ForEach(dogs) { dog in
      				TableRow(dog)
      			}
      		}
      	}
      }
    • 19:22 - DogTable Brief

      struct DogTable: View {
      	var dogs: [Dog]
      	var body: some View {
      		Table(of: Dog.self) {
      			// Columns
      		} rows: {
      			ForEach(dogs)
      		}
      	}
      }
    • 20:06 - DogTable Different IDs

      struct DogTable: View {
      	var dogs: [Dog]
      	var body: some View {
      		Table(of: Dog.self) {
      			// Columns
      		} rows: {
      			ForEach(dogs) { dog in
      				TableRow(dog.bestFriend)
      			}
      		}
      	}
      }