SwiftUI/日本語チュートリアル2
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
&tag(SwiftUI/日本語チュートリアル2);
*目次 [#o4046361]
#contents
*関連ページ [#v37374c7]
*参考情報 [#de50f83f]
-[[【第2回】日本語版SwiftUIチュートリアル【リストとナビゲ...
*第2回:リストとナビゲーション ~動的なビュー生成~ [#he2a7...
** Step 1. モデルを作る [#cde9f629]
-Modelsディレクトリを作成。
-そのなかにLandmark.swiftを作る。
#pre{{
import SwiftUI
struct Landmark: Hashable, Codable{
var id: Int
var name: String
fileprivate var imageName: String
fileprivate var coordinates: Coordinates
var state: String
var park: String
var category: Category
var locationCoordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: coordinates.latitude,
longitude: coordinates.longitude)
}
enum Category: String, CaseIterable, Hashable, Codabl...
case featured = "Featured"
case lakes = "Lakes"
case rivers = "Rivers"
case mountains= "Mountains"
}
}
extension Landmark {
var image: Image {
ImageStore.shared.image(name: imageName)
}
}
struct Coordinates: Hashable, Codable {
var latitude: Double
var longitude: Double
}
}}
** Step 2. データ処理部 [#ub7fa690]
-ModelsディレクトリにData.swiftを作成。内容は以下。
#pre{{
import UIKit
import SwiftUI
import CoreLocation
let landmarkData: [Landmark] = load("landmarkData.json")
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filenam...
else {
fatalError("Couldn't find \(filename) in main...
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main b...
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.sel...
}
}
final class ImageStore {
typealias _ImageDictionary = [String: CGImage]
fileprivate var images: _ImageDictionary = [:]
fileprivate static var scale = 2
static var shared = ImageStore()
func image(name: String) -> Image {
let index = _guaranteeImage(name: name)
return Image(images.values[index], scale: CGFloat...
}
static func loadImage(name: String) -> CGImage {
guard
let url = Bundle.main.url(forResource: name, ...
let imageSource = CGImageSourceCreateWithURL(...
let image = CGImageSourceCreateImageAtIndex(i...
else {
fatalError("Couldn't load image \(name).jpg f...
}
return image
}
fileprivate func _guaranteeImage(name: String) -> _Im...
if let index = images.index(forKey: name) { retur...
images[name] = ImageStore.loadImage(name: name)
return images.index(forKey: name)!
}
}
}}
-要はjsonからLandmarkオブジェクトを作成するたに使われてい...
**Step 3. リストビューを作る前に構成要素を作る [#q3807f8b]
-ContentView.swiftをLandmarkDetail.swiftに変更。リファク...
-LandmarkRowを作成。
#pre{{
import SwiftUI
struct LandmarkRow: View {
var landmark: Landmark
var body: some View {
Text(landmark.name)
}
}
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
LandmarkRow(landmark: landmarkData[0])
}
}
}}
-デザインを整える。これでリストに表示可能な見た目となる。
#pre{{
struct LandmarkRow: View {
var landmark: Landmark
var body: some View {
HStack{
landmark.image
.resizable()
.frame(width: 50, height: 50)
Text(landmark.name)
Spacer()
}
}
}
}}
**Step 4. 複数のデータを使ったプレビュー表示 [#k62e76cd]
-プレビューの改善。
#pre{{
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
Group {
LandmarkRow(landmark: landmarkData[0])
LandmarkRow(landmark: landmarkData[1])
LandmarkRow(landmark: landmarkData[2])
}
.previewLayout(.fixed(width: 300, height: 70))
}
}
}}
**Step 5. 観光地データをリストとして積み上げる [#l70eca08]
-LandmarkList.swiftを作成。
-以下で2個のデータが表示される。
#pre{{
import SwiftUI
struct LandmarkList: View {
var body: some View {
List {
LandmarkRow(landmark: landmarkData[0])
LandmarkRow(landmark: landmarkData[1])
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
}
}
}}
-動的データを表示する。
#pre{{
struct LandmarkList: View {
var body: some View {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
}}
-これを可能とするためLandmarkにIdentifiableを付与。
struct Landmark: Hashable, Codable, Identifiable{
-これでリストが表示される。
-初期化のところで使われているのはクロージャ構文。
**Step 6. リスト<=>詳細ビューのナビゲーション [#h5d7ddab]
-LandmarkList.swiftとLandmarkDetail.swiftをつなげる
-LandmarkListナビゲーションビューとしタイトルをつける。
#pre{{
struct LandmarkList: View {
var body: some View {
NavigationView{
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}.navigationBarTitle(Text("Landmarks"))
}
}
}
}}
-詳細ビューが表示できるようにする。
#pre{{
struct LandmarkList: View {
var body: some View {
NavigationView{
List(landmarkData) { landmark in
NavigationLink(destination: LandmarkDetai...
LandmarkRow(landmark: landmark)
}
}.navigationBarTitle(Text("Landmarks"))
}
}
}
}}
-ただしまだ詳細ビューはTurtle Rockのまま。
**Step 7. 各要素を動的なビューにしていく [#a3d92232]
- CircleImage.swiftを動的にする。
#pre{{
struct CircleImage: View {
var image: Image
var body: some View {
image
.clipShape(Circle())
.overlay(
Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
}
}
struct CircleImage_Previews: PreviewProvider {
static var previews: some View {
CircleImage(image: Image("turtlerock"))
}
}
}}
-MapView.swiftを修正。
#pre{{
struct MapView: UIViewRepresentable {
var coordinate: CLLocationCoordinate2D
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ uiView: MKMapView, context: Conte...
let span = MKCoordinateSpan(latitudeDelta: 2.0, l...
let region = MKCoordinateRegion(center: coordinat...
uiView.setRegion(region, animated: true)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView(coordinate: landmarkData[0].locationCoord...
}
}
}}
-LandmarkDetail.swiftを修正。
#pre{{
struct LandmarkDetail: View {
var landmark: Landmark
var body: some View {
VStack {
MapView(coordinate: landmark.locationCoordina...
.edgesIgnoringSafeArea(.top)
.frame(height: 300)
CircleImage(image: landmark.image)
.offset(y: -130)
.padding(.bottom, -130)
VStack(alignment: .leading) {
Text(landmark.name)
.font(.title)
HStack {
Text(landmark.park)
.font(.subheadline)
Spacer()
Text(landmark.state)
.font(.subheadline)
}
}
.padding()
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
LandmarkDetail(landmark: landmarkData[0])
}
}
}}
-LandmarksAppを修正(SceneDelegate.swiftは存在しなかった)。
#pre{{
import SwiftUI
@main
struct LandmarksApp: App {
var body: some Scene {
WindowGroup {
LandmarkList()
}
}
}
}}
-LandmarkList.swiftを修正。
#pre{{
struct LandmarkList: View {
var body: some View {
NavigationView{
List(landmarkData) { landmark in
NavigationLink(destination: LandmarkDetai...
LandmarkRow(landmark: landmark)
}
}.navigationBarTitle(Text("Landmarks"))
}
}
}
}}
**Step 8. プレビュー端末を変える。 [#s3c13948]
-以下のように変更できる。
#pre{{
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
.previewDevice(PreviewDevice(rawValue: "iPhon...
}
}
}}
終了行:
&tag(SwiftUI/日本語チュートリアル2);
*目次 [#o4046361]
#contents
*関連ページ [#v37374c7]
*参考情報 [#de50f83f]
-[[【第2回】日本語版SwiftUIチュートリアル【リストとナビゲ...
*第2回:リストとナビゲーション ~動的なビュー生成~ [#he2a7...
** Step 1. モデルを作る [#cde9f629]
-Modelsディレクトリを作成。
-そのなかにLandmark.swiftを作る。
#pre{{
import SwiftUI
struct Landmark: Hashable, Codable{
var id: Int
var name: String
fileprivate var imageName: String
fileprivate var coordinates: Coordinates
var state: String
var park: String
var category: Category
var locationCoordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: coordinates.latitude,
longitude: coordinates.longitude)
}
enum Category: String, CaseIterable, Hashable, Codabl...
case featured = "Featured"
case lakes = "Lakes"
case rivers = "Rivers"
case mountains= "Mountains"
}
}
extension Landmark {
var image: Image {
ImageStore.shared.image(name: imageName)
}
}
struct Coordinates: Hashable, Codable {
var latitude: Double
var longitude: Double
}
}}
** Step 2. データ処理部 [#ub7fa690]
-ModelsディレクトリにData.swiftを作成。内容は以下。
#pre{{
import UIKit
import SwiftUI
import CoreLocation
let landmarkData: [Landmark] = load("landmarkData.json")
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filenam...
else {
fatalError("Couldn't find \(filename) in main...
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main b...
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.sel...
}
}
final class ImageStore {
typealias _ImageDictionary = [String: CGImage]
fileprivate var images: _ImageDictionary = [:]
fileprivate static var scale = 2
static var shared = ImageStore()
func image(name: String) -> Image {
let index = _guaranteeImage(name: name)
return Image(images.values[index], scale: CGFloat...
}
static func loadImage(name: String) -> CGImage {
guard
let url = Bundle.main.url(forResource: name, ...
let imageSource = CGImageSourceCreateWithURL(...
let image = CGImageSourceCreateImageAtIndex(i...
else {
fatalError("Couldn't load image \(name).jpg f...
}
return image
}
fileprivate func _guaranteeImage(name: String) -> _Im...
if let index = images.index(forKey: name) { retur...
images[name] = ImageStore.loadImage(name: name)
return images.index(forKey: name)!
}
}
}}
-要はjsonからLandmarkオブジェクトを作成するたに使われてい...
**Step 3. リストビューを作る前に構成要素を作る [#q3807f8b]
-ContentView.swiftをLandmarkDetail.swiftに変更。リファク...
-LandmarkRowを作成。
#pre{{
import SwiftUI
struct LandmarkRow: View {
var landmark: Landmark
var body: some View {
Text(landmark.name)
}
}
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
LandmarkRow(landmark: landmarkData[0])
}
}
}}
-デザインを整える。これでリストに表示可能な見た目となる。
#pre{{
struct LandmarkRow: View {
var landmark: Landmark
var body: some View {
HStack{
landmark.image
.resizable()
.frame(width: 50, height: 50)
Text(landmark.name)
Spacer()
}
}
}
}}
**Step 4. 複数のデータを使ったプレビュー表示 [#k62e76cd]
-プレビューの改善。
#pre{{
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
Group {
LandmarkRow(landmark: landmarkData[0])
LandmarkRow(landmark: landmarkData[1])
LandmarkRow(landmark: landmarkData[2])
}
.previewLayout(.fixed(width: 300, height: 70))
}
}
}}
**Step 5. 観光地データをリストとして積み上げる [#l70eca08]
-LandmarkList.swiftを作成。
-以下で2個のデータが表示される。
#pre{{
import SwiftUI
struct LandmarkList: View {
var body: some View {
List {
LandmarkRow(landmark: landmarkData[0])
LandmarkRow(landmark: landmarkData[1])
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
}
}
}}
-動的データを表示する。
#pre{{
struct LandmarkList: View {
var body: some View {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
}}
-これを可能とするためLandmarkにIdentifiableを付与。
struct Landmark: Hashable, Codable, Identifiable{
-これでリストが表示される。
-初期化のところで使われているのはクロージャ構文。
**Step 6. リスト<=>詳細ビューのナビゲーション [#h5d7ddab]
-LandmarkList.swiftとLandmarkDetail.swiftをつなげる
-LandmarkListナビゲーションビューとしタイトルをつける。
#pre{{
struct LandmarkList: View {
var body: some View {
NavigationView{
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}.navigationBarTitle(Text("Landmarks"))
}
}
}
}}
-詳細ビューが表示できるようにする。
#pre{{
struct LandmarkList: View {
var body: some View {
NavigationView{
List(landmarkData) { landmark in
NavigationLink(destination: LandmarkDetai...
LandmarkRow(landmark: landmark)
}
}.navigationBarTitle(Text("Landmarks"))
}
}
}
}}
-ただしまだ詳細ビューはTurtle Rockのまま。
**Step 7. 各要素を動的なビューにしていく [#a3d92232]
- CircleImage.swiftを動的にする。
#pre{{
struct CircleImage: View {
var image: Image
var body: some View {
image
.clipShape(Circle())
.overlay(
Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
}
}
struct CircleImage_Previews: PreviewProvider {
static var previews: some View {
CircleImage(image: Image("turtlerock"))
}
}
}}
-MapView.swiftを修正。
#pre{{
struct MapView: UIViewRepresentable {
var coordinate: CLLocationCoordinate2D
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ uiView: MKMapView, context: Conte...
let span = MKCoordinateSpan(latitudeDelta: 2.0, l...
let region = MKCoordinateRegion(center: coordinat...
uiView.setRegion(region, animated: true)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView(coordinate: landmarkData[0].locationCoord...
}
}
}}
-LandmarkDetail.swiftを修正。
#pre{{
struct LandmarkDetail: View {
var landmark: Landmark
var body: some View {
VStack {
MapView(coordinate: landmark.locationCoordina...
.edgesIgnoringSafeArea(.top)
.frame(height: 300)
CircleImage(image: landmark.image)
.offset(y: -130)
.padding(.bottom, -130)
VStack(alignment: .leading) {
Text(landmark.name)
.font(.title)
HStack {
Text(landmark.park)
.font(.subheadline)
Spacer()
Text(landmark.state)
.font(.subheadline)
}
}
.padding()
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
LandmarkDetail(landmark: landmarkData[0])
}
}
}}
-LandmarksAppを修正(SceneDelegate.swiftは存在しなかった)。
#pre{{
import SwiftUI
@main
struct LandmarksApp: App {
var body: some Scene {
WindowGroup {
LandmarkList()
}
}
}
}}
-LandmarkList.swiftを修正。
#pre{{
struct LandmarkList: View {
var body: some View {
NavigationView{
List(landmarkData) { landmark in
NavigationLink(destination: LandmarkDetai...
LandmarkRow(landmark: landmark)
}
}.navigationBarTitle(Text("Landmarks"))
}
}
}
}}
**Step 8. プレビュー端末を変える。 [#s3c13948]
-以下のように変更できる。
#pre{{
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
.previewDevice(PreviewDevice(rawValue: "iPhon...
}
}
}}
ページ名: