ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
TipKitによる機能検索のカスタマイズ
機能検索に焦点を当てたTipKitフレームワークを使用すると、アプリにヒントを簡単に表示できます。ヒントをグループ化することで、最適な順序で機能をユーザーに見つけてもらうことができます。さらに、カスタムのヒント識別子を使用してヒントを再使用可能にしたり、見た目や操作性をアプリに合わせたり、CloudKitを使用してヒントを同期したりすることも可能です。TipKitの最新機能を使用して、アプリのさまざまなサービスをユーザーに知ってもらう方法を学びましょう。
関連する章
- 0:00 - Introduction
- 1:18 - Tip groups
- 5:12 - Reusable tips with custom identifiers
- 8:25 - Custom tip styles
- 10:48 - Sync tips with CloudKit
リソース
関連ビデオ
WWDC23
-
ダウンロードArray
-
-
1:43 - Create new tips
// Create new tips struct ShowLocationTip: Tip { var title: Text { Text("Show your location") } var message: Text? { Text("Tap the compass to highlight your current location on the map.") } var image: Image? { Image(systemName: "location.circle") } }
-
1:54 - Create new tips
// Create new tips struct ShowLocationTip: Tip { var title: Text { Text("Show your location") } var message: Text? { Text("Tap the compass to highlight your current location on the map.") } var image: Image? { Image(systemName: "location.circle") } } struct RotateMapTip: Tip { var title: Text { Text("Reorient the map") } var message: Text? { Text("Tap and hold on the compass to rotate the map back to 0° North.") } var image: Image? { Image(systemName: "hand.tap") } }
-
2:09 - Show popover tips
// Show popover tips struct MapCompassControl: View { let showLocationTip = ShowLocationTip() let rotateMapTip = RotateMapTip() var body: some View { CompassDial() .popoverTip(showLocationTip) .popoverTip(rotateMapTip) .onTapGesture { showCurrentLocation() } .onLongPressGesture(minimumDuration: 0.1) { reorientMapHeading() } } }
-
2:41 - Create a TipGroup
// Create a TipGroup struct MapCompassControl: View { @State var compassTips: TipGroup(.ordered) { ShowLocationTip() RotateMapTip() } var body: some View { CompassDial() .popoverTip(compassTips.currentTip) .onTapGesture { showCurrentLocation() } .onLongPressGesture(minimumDuration: 0.1) { reorientMapHeading() } } }
-
3:15 - Show TipGroup tips on different views
// Show TipGroup tips on different views struct MapControlsStack: View { @State var compassTips: TipGroup(.ordered) { ShowLocationTip() RotateMapTip() } var body: some View { VStack { ShowLocationButton() .popoverTip(compassTips.currentTip as? ShowLocationTip) RotateMapButton() .popoverTip(compassTips.currentTip as? RotateMapTip) } } }
-
3:50 - Invalidate tips
// Invalidate tips struct MapCompassControl: View { @State var compassTips: TipGroup(.ordered) { showLocationTip rotateMapTip } var body: some View { CompassDial() .popoverTip(compassTips.currentTip) .onTapGesture { showLocationTip.invalidate(reason: .actionPerformed) showCurrentLocation() } .onLongPressGesture(minimumDuration: 0.1) { rotateMapTip.invalidate(reason: .actionPerformed) reorientMapHeading() } } }
-
5:37 - Create a tip
// Create a tip struct ButlerForkTip: Tip { var title: Text { Text("Butler Fork is now available") } var message: Text? { Text("To see key trail info, tap Big Cottonwood Canyon on the map.") } var actions: [Action] { Action(title: "Go there now") } var rules: [Rule] { #Rule(Region.bigCottonwoodCanyon.didVisitEvent) { $0.donations.count > 3 } } }
-
6:01 - Show a TipView
// Show a TipView struct ButlerForkTip: Tip { var title: Text { Text("Butler Fork is now available") } var message: Text? { Text("To see key trail info, tap Big Cottonwood Canyon on the map.") } var actions: [Action] { Action(title: "Go there now") } var rules: [Rule] { #Rule(Region.bigCottonwoodCanyon.didVisitEvent) { $0.donations.count > 3 } } } struct TrailList: View { var trails: [Trail] var body: some View { ScrollView { let butlerForkTip = ButlerForkTip() TipView(butlerForkTip) { _ in highlightButlerForkTrail() } ListSection(title: "Trails", trails: trails) } } }
-
6:45 - Create a reusable tip
// Create a reusable tip struct NewTrailTip: Tip { let newTrail: Trail var title: Text { Text("\(newTrail.name) is now available") } var message: Text? { Text("To see key trail info, tap \(newTrail.region) on the map.") } var actions: [Action] { Action(title: "Go there now") } var id: String { "NewTrailTip-\(newTrail.id)" } var rules: [Rule] { #Rule(newTrail.region.didVisitEvent) { $0.donations.count > 3 } } }
-
7:26 - Show a TipView
// Show a TipView struct NewTrailTip: Tip { let newTrail: Trail var title: Text { Text("\(newTrail.name) is now available") } var message: Text? { Text("To see key trail info, tap \(newTrail.region) on the map.") } var actions: [Action] { Action(title: "Go there now") } var id: String { "NewTrailTip-\(newTrail.id)" } var rules: [Rule] { #Rule(newTrail.region.didVisitEvent) { $0.donations.count > 3 } } } struct TrailList: View { var trails: [Trail] let newTrail: Trail var body: some View { ScrollView { let newTrailTip = NewTrailTip(newTrail: newTrail) TipView(newTrailTip) { _ in highlightTrail(newTrailTip) } ListSection(title: "Trails", trails: trails) } } }
-
8:55 - Create a custom TipViewStyle
// Create a custom TipViewStyle struct NewTrailTipViewStyle: TipViewStyle { func makeBody(configuration: Configuration) -> some View { let tip = configuration.tip as! NewTrailTip TrailImage(imageName: tip.newTrail.heroImage) .frame(maxHeight: 150) .overlay { VStack { configuration.title.font(.title) configuration.message.font(.subheadline) } } } } extension NewTrailTipViewStyle { struct TrailImage: View { let imageName: String var body: some View { Image(imageName) .resizable() .aspectRatio(contentMode: .fill) } } }
-
9:20 - Apply a TipViewStyle
// Apply a TipViewStyle struct NewTrailTipViewStyle: TipViewStyle { func makeBody(configuration: Configuration) -> some View { let tip = configuration.tip as! NewTrailTip TrailImage(imageName: tip.newTrail.heroImage) .frame(maxHeight: 150) .overlay { VStack { configuration.title.font(.title) configuration.message.font(.subheadline) } } } } extension NewTrailTipViewStyle { struct TrailImage: View { let imageName: String var body: some View { Image(imageName) .resizable() .aspectRatio(contentMode: .fill) } } } struct TrailList: View { var trails: [Trail] let newTrail: Trail var body: some View { ScrollView { let newTrailTip = NewTrailTip(newTrail: newTrail) TipView(newTrailTip) { _ in highlightTrail(newTrailTip) } .tipViewStyle(NewTrailTipViewStyle()) ListSection(title: "Trails", trails: trails) } } }
-
9:45 - Add the tip's action handler
// Apply a TipViewStyle struct NewTrailTipViewStyle: TipViewStyle { func makeBody(configuration: Configuration) -> some View { let tip = configuration.tip as! NewTrailTip let highlightTrailAction = configuration.actions.first! TrailImage(imageName: tip.newTrail.heroImage) .frame(maxHeight: 150) .onTapGesture { highlightTrailAction.handler() } .overlay { VStack { configuration.title.font(.title) HStack { configuration.message.font(.subheadline) Spacer() Image(systemName: "chevron.forward.circle") .foregroundStyle(.white) } } } } } extension NewTrailTipViewStyle { struct TrailImage: View { let imageName: String var body: some View { Image(imageName) .resizable() .aspectRatio(contentMode: .fill) } } } struct TrailList: View { var trails: [Trail] let newTrail: Trail var body: some View { ScrollView { let newTrailTip = NewTrailTip(newTrail: newTrail) TipView(newTrailTip) { _ in highlightTrail(newTrailTip) } .tipViewStyle(NewTrailTipViewStyle()) ListSection(title: "Trails", trails: trails) } } }
-
11:38 - Add CloudKit sync for tips
// Add CloudKit sync for tips @main struct TipKitTrails: App { var body: some Scene { WindowGroup { ContentView() .task { await configureTips() } } } func configureTips() async { do { try Tips.configure([ .cloudKitContainer(.named("iCloud.com.apple.TipKitTrails.tips")), .displayFrequency(.weekly) ]) } catch { print("Unable to configure tips: \(error)") } } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。