﻿Swift Playgrounds4ではじめるiPhoneアプリ開発

掲載リスト

 

リスト1-1
show("あなたの名前は何ですか?")

let name = ask("名前")

show("どうも " + name)


リスト1-2
show("Hello!")
show("こんにちは。")
show(1234500)


リスト2-1
show("Hello")
show("こんにちは。")
show(12345)
show(0.0000000012345)


リスト2-2
show("Hello" + "こんにちは。")
show(123 + 45 * 6 / 7 - 89)


リスト2-3
show("金額を入力して下さい。")
let input = ask("金額")
let price = Double(input) ?? 0
var tax = price * 0.1
show(tax)
tax = price * 0.08
show(tax)


リスト2-4
show("金額を入力して下さい。")
let input = ask("金額")
let price = Double(input) ?? 0
let result = price + price * 0.1
show(String(price) + "円の税込価格は、" + String(result) + "円です。")


リスト2-5
show("数値を入力して下さい。")
let input = ask("値")
let num = Int(input) ?? 0
if num % 2 == 0 {
  show(String(num) + "は、偶数です。")
} else {
  show(String(num) + "は、奇数です。")
}


リスト2-6
show("月の値を入力して下さい。")
let input = ask("1 ~ 12 の整数")
let m = Int(input) ?? 1
switch m {
case 1...2: 
  show(String(m) + "は、冬です。")
case 3...5: 
  show(String(m) + "は、春です。")
case 6...8: 
  show(String(m) + "は、夏です。")
case 9...11: 
  show(String(m) + "は、秋です。")
case 12: 
  show(String(m) + "は、冬です。")
default: 
  show("よくわかりません。")
}


リスト2-7
show("月の値を入力して下さい。")
let input = ask("1 ~ 12 の整数")
let m = Int(input) ?? 1
switch m {
case 1...2: 
  show("\(m)は、冬です。")
case 3...5: 
  show("\(m)は、春です。")
case 6...8: 
  show("\(m)は、夏です。")
case 9...11: 
  show("\(m)は、秋です。")
case 12: 
  show("\(m)は、冬です。")
default: 
  show("よくわかりません。")
}


リスト2-8
show("数値を入力して下さい。")
let input = ask("整数")
let max = Int(input) ?? 1
var total = 0
var count = 0
while count <= max {
  total += count
  count += 1
}
show("\(max)までの合計は、\(total)です。")


リスト2-9
show("数値を入力して下さい。")
let input = ask("整数")
let max = Int(input) ?? 1
var total = 0
for n in 1...max {
  total += n
}
show("\(max)までの合計は、\(total)です。")


リスト2-10
Hello()
Hello()
Hello()

func Hello() {
  show("Hello!")
}


リスト2-11
Hello(name: "Taro")
Hello(name: "Hanako")
Hello(name: "Sachiko")

func Hello(name: String) {
  show("Hello, \(name)!!")
}


リスト2-12
let taro = Hello(name: "Taro")
let hanako = Hello(name: "Hanako")
let sachiko = Hello(name: "Sachiko")
show("\(taro) \(hanako) \(sachiko)")

func Hello(name: String)->String {
  return "Hello, \(name)!!"
}


リスト2-13
let data = [12, 34, 56]
let total = data[0] + data[1] + data[2]
let ave = total / 3
show("合計は、\(total)、平均は、\(ave)です。")


リスト2-14
let data = [12, 34, 56, 78, 98, 76, 54]
var total = 0
for n in data {
  total += n
}
show("合計：\(total). 平均：\(total / data.count).")


リスト2-15
let tels = [
  "taro": "090-999-999",
  "hanako": "080-888-888",
  "sachiko": "070-777-777"
]
show("調べたい名前は？")
let name = ask("名前")
let answer = tels[name] ?? "不明"
show("\(name)さんの電話番号は、\(answer)です。")


リスト2-16
let data = [
  "国語": 98,
  "数学": 87,
  "理科": 76,
  "社会": 65,
  "英語": 54
]
for (key, value) in data {
  show("\(key)の点数は、\(value)です。")
}


リスト2-17
let data = (name: "taro", age: 39, gender: false)
show("知りたい情報は？")
let item = ask("name/age/gender")
switch item {
case "name": 
  show("名前は、\(data.name)です。")
case "age": 
  show("年齢は、\(data.age)です。")
case "gender": 
  show("性別は、\(data.gender ? "女" : "男")性です。")
default: 
  show("わかりません。")
}


リスト2-18
let data = [
  "taro": (name: "たろう", 
  mail: "taro@yamada",
  age: 39, gender: false),
  "hanako": (name: "はなこ",
  mail: "hanako@flower",
  age: 28, gender: true),
  "sachiko": (name: "さちこ",
  mail: "sachiko@happy",
  age: 17, gender: true)
]
show("名前は？")
let name = ask("名前")
let value = data[name] ?? (name: "", mail: "", age: 0, gender: true)
show("\(value.name) <\(value.mail)>  \(value.age)歳, \(value.gender ? "女" : "男")性.")


リスト2-19
class Hello {
  var message = "Hello!"
  func print() {
  show(message)
  }
}

let hello = Hello()
hello.print()


リスト2-20
class Hello {
  var message = "Hello"
  var name = "noname"
  
  init(name: String) {
    self.name = name
  }
  func print() {
    show(message + ", " + name + "!")
  }
}

let taro = Hello(name: "taro")
taro.print()
let hanako = Hello(name: "hanako")
hanako.print()


リスト2-21
class Hello {
  var message: String {
    return "こんにちは、\(name)さん！"
  }
  var name = "noname"
  
  init(name: String) {
    self.name = name
  }
  func print() {
    show(message)
  }
}

let taro = Hello(name: "taro")
taro.print()
let hanako = Hello(name: "hanako")
hanako.print()


リスト2-22
class Hello {
  var message: String {
    return "こんにちは、\(name)さん！"
  }
  var name = "noname"
  
  init(name: String) {
    self.name = name
  }
  func print() {
    show(message)
  }
}

class Greeting: Hello {
  override var message: String {
    return prefix + name + suffix
  }
  var prefix = "こんにちは、"
  var suffix = "さん！"
  init(prefix: String, suffix: String, name: String) {
    super.init(name: name)
    self.prefix = prefix
    self.suffix = suffix
  }
  
  override func print() {
    show("*** \(message) ***")
  }
}

let taro = Hello(name: "taro")
taro.print()
let hanako = Greeting(prefix: "はろー、", suffix: "ちゃん❤", name: "hanako")
hanako.print()


リスト2-23
class Calc {
  static var tax = 10
  static func get(price: Int)->Int {
    return Int(Double(price) * (1.0 + Double(tax) / 100.0))
  }
}

show("金額を入力して下さい。")
let input = ask("金額")
let price = Int(input) ?? 0

show("\(price)円の税込金額は、\(Calc.get(price: price))円です。")
Calc.tax = 8
show("\(price)円の軽減税率の税込金額は、\(Calc.get(price: price))円です。")


リスト3-1
Text("hello, friend.")


リスト3-2
Text("こんにちは！")


リスト3-3
Image("FriendAndGem")
  .resizable()


リスト3-4
.scaledToFit()


リスト3-5
import SwiftUI

struct ContentView: View {
  var body: some View {
    VStack {
      Image(systemName: "globe")
        .imageScale(.large)
        .foregroundColor(.accentColor)
      Text("Hello, world!")
    }
  }
}


リスト3-6
import SwiftUI

@main
struct MyApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}


リスト3-7
import SwiftUI

struct ContentView: View {
  var body: some View {
    VStack {
      Text("Hello, world!")
    }
  }
}


リスト3-8
import PlaygroundSupport


リスト3-9
PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())


リスト3-10
public struct MyData {
  private var name: String
  
  public var msg: String {
    "こんにちは、\(name)さん。"
  }
  
  public init(name: String) {
    self.name = name
  }
}


リスト3-11
// import は省略
let mydata = MyData(name: "タロー")

struct ContentView: View {
  var body: some View {
    VStack {
      Text(mydata.msg)
    }
  }
}
// ライブビューの設定は省略


リスト3-12
struct ContentView: View {
  var body: some View {
    VStack {
      Text("Hello, world!")
        .bold()
        .italic()
        .font(.system(size: 36))
    }
  }
}


リスト3-13
struct ContentView: View {
  var body: some View {
    VStack {
      Text("Hello, world!")
        .font(.system(size: 36))
        .padding(25)
        .foregroundColor(.blue)
        .background(Color(red: 0.8, green: 1.0, blue: 1.0))
    }
  }
}


リスト3-14
struct ContentView: View {
  var body: some View {
    VStack {
      Text("Hello, world!")
        .font(.system(size: 36))
        .padding(25)
        .border(.red, width: 10)
    }
  }
}


リスト3-15
struct ContentView: View {
  var body: some View {
    VStack {
      Text("Hello, world!")
        .font(.system(size: 36))
        .padding(25)
        .border(.red, width: 10)
        .padding(50)
        .border(.blue, width: 5)
    }
  }
}


リスト3-16
struct ContentView: View {
  var body: some View {
    VStack {
      Text("Hello, world!")
        .font(.system(size: 36))
        .position(x: 0, y: 0)
        .frame(width: 100, height: 100)
        .background(Color(red: 0.9, green: 0.9, blue: 1))
    }
  }
}


リスト3-17
struct MyText: View {
  var msg: String = ""
  var body: some View {
    Text(msg)
      .font(.system(size: 25))
      .padding(20)
      .background(Color(red: 1, green: 0.9, blue: 0.9))
      .border(.red, width: 3)
  }
}


リスト3-18
struct ContentView: View {
  var body: some View {
    VStack {
      MyText(msg: "Hello!")
      MyText(msg: "こんにちは。")
    }
  }
}


リスト3-19
struct ContentView: View {
  var body: some View {
    HStack {
      MyText(msg: "Hello!")
      MyText(msg: "こんにちは")
    }
  }
}


リスト3-20
enum MySize {
  case big
  case mid
  case small
}
enum MyColor {
  case red
  case green
  case blue
}

struct MyText: View {
  var msg: String = ""
  var tsize = 10.0
  var vsize = 20.0
  var bcolor = Color.white
  var fcolor = Color.white
  
  var body: some View {
    Text(msg)
      .font(.system(size: tsize))
      .frame(width: vsize, height: vsize)
      .padding(10)
      .background(bcolor)
      .border(fcolor, width: 3)
  }
  
  init(msg: String, size: MySize, color: MyColor) {
    self.msg = msg
    switch size {
      case .small: 
      tsize = 12.0
      vsize = 30.0
      case .mid: 
      tsize = 16.0
      vsize = 50.0
      case .big: 
      tsize = 20
      vsize = 75
    }
    switch color {
      case .red: 
      bcolor = Color(red: 1.0,green: 0.9,blue: 0.9)
      fcolor = Color.red
      case .green: 
      bcolor = Color(red: 0.9,green: 1.0,blue: 0.9)
      fcolor = Color.green
      case .blue: 
      bcolor = Color(red: 0.9,green: 0.9,blue: 1.0)
      fcolor = Color.blue
    }
  }
}


リスト3-21
struct ContentView: View {
  var body: some View {
    VStack {
      HStack {
        MyText(msg: "ABC",size: .mid, color: .blue)
        MyText(msg: "DEF",size: .mid,color: .blue)
        MyText(msg: "GHI",size: .mid,color: .blue)
      }
      HStack {
        MyText(msg: "JKL",size: .mid, color: .blue)
        MyText(msg: "MNO",size: .mid,color: .blue)
        MyText(msg: "PQR",size: .mid,color: .blue)
      }
      HStack {
        MyText(msg: "STU",size: .mid, color: .blue)
        MyText(msg: "VWX",size: .mid,color: .blue)
        MyText(msg: "YZ!",size: .mid,color: .blue)
      }
    }
  }
}


リスト3-22
struct ContentView: View {
  var body: some View {
    VStack {
      Spacer()
      MyText(msg: "ABC",size: .big, color: .blue)
      Divider()
      Spacer()
      MyText(msg: "ABC",size: .big, color: .blue)
      Spacer()
      Divider()
      MyText(msg: "ABC",size: .big, color: .blue)
      Spacer()
    }
  }
}


リスト3-23
struct ContentView: View {
  var body: some View {
    VStack {
      Spacer(minLength: 20).fixedSize()
      MyText(msg: "ABC",size: .big, color: .blue)
      Divider()
      Spacer(minLength: 20).fixedSize()
      MyText(msg: "ABC",size: .big, color: .blue)
      Divider()
      Spacer(minLength: 20).fixedSize()
      MyText(msg: "ABC",size: .big, color: .blue)
      Spacer()
    }
  }
}


リスト3-24
import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


リスト4-1
struct ContentView: View {
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Button(action: {
        print("Button tapped!")
      },label: {
        Text("action")
          .font(.title)
      })
      Spacer()
    }
  }
}


リスト4-2
struct ContentView: View {
  @State var msg: String = "Hello."
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text(msg).font(.largeTitle)
      Spacer().fixedSize()
      Button(action: {
        msg = "Button tapped!"
      },label: {
        Text("action")
          .font(.title)
      })
      Spacer()
    }
  }
}


リスト4-3
struct ContentView: View {
  @State var msg: String = "Hello."
  @State var input: String = ""
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text(msg).font(.largeTitle)
      Spacer().fixedSize()
      TextField("your name", text: $input)
        .padding(10)
        .font(.title2)
        .textFieldStyle(RoundedBorderTextFieldStyle())
      Button(action: {
        msg = "Hello, \(input)!"
      },label: {
        Text("action")
          .font(.title)
      })
      Spacer()
    }
  }
}


リスト4-4
struct ContentView: View {
  @State var msg: String = "Hello."
  @State var input: String = ""
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text(msg).font(.largeTitle)
        .onTapGesture {
          msg = "Bye, \(input)."
        }
        .onLongPressGesture(perform: {
          msg = "Hello."
        })
      Spacer().fixedSize()
      TextField("your name", text: $input)
        .padding(10)
        .font(.title2)
        .textFieldStyle(RoundedBorderTextFieldStyle())
      Button(action: {
        msg = "Hello, \(input)!"
      },label: {
        Text("action")
          .font(.title)
      })
      Spacer()
    }
  }
}


リスト4-5
class ContentData: ObservableObject {
  @Published var count = 0
  @Published var color = Color.black
  
  func update()->Void {
    count += 1
    switch count % 3 {
      case 0: color = Color.red
      case 1: color = Color.green
      case 2: color = Color.blue
    default: color = Color.black
    }
  }
  
  func reset()->Void {
    count = 0
    color = Color.black
  }
}


リスト4-6
struct ContentView: View {
  @ObservedObject var states: ContentData
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text("Count: \(states.count).").font(.largeTitle)
        .foregroundColor(states.color)
        .onTapGesture {
          states.reset()
        }
      Spacer().fixedSize()
      Button(action: {
        states.update()
      },label: {
        Text("action")
          .font(.title)
      })
      Spacer()
    }
  }
  public init() {
    states = ContentData()
  }
}


リスト4-7
struct ContentView: View {
  @State var flag: Bool = false
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text("State: \(flag ? "Onです": "Offです").")
        .font(.largeTitle)
      Spacer().fixedSize()
      Toggle("トグルスイッチ", isOn: $flag)
        .padding(25)
      Spacer()
    }
  }
}


リスト4-8
struct ContentView: View {
  @State var value: Int = 1
  @State var step: Int = 2
  @State var step2: Int = 1
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text("Value: \(value)です.")
        .font(.title)
      Spacer().fixedSize()
      Stepper("数字を増減します", 
        onIncrement: {
          value = step
          let step0 = step2
          step2 = step
          step = step0 + step2
        }, 
        onDecrement: {
          let step0 = step2
          step2 = step - step2
          step = step0
          value -= step - step2
        })
        .padding(25)
      Spacer()
    }
  }
}


リスト4-9
struct ContentView: View {
  @State var value: Double = 0
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text("Value: \(Int(value))です.")
        .font(.title)
      Spacer().fixedSize()
      Slider(value: $value, in: 0...100)
        .padding(25)
      Spacer()
    }
  }
}


リスト4-10
struct ContentView: View {
  let data = [
    "Apple", "Orange", "Banana", "Grape", "Melon"
  ]
  @State var value: Int = 0
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text("Select: \(data[value])です.")
        .font(.title)
      Spacer().fixedSize()
      Picker("ピッカー", selection: $value, content: {
        ForEach(0..<data.count) {n in
          Text(data[n])
        }
      }).padding(25)
      Spacer()
    }
  }
}


リスト4-11
struct ContentView: View {
  @State var value: Color = Color.black
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text("Select: \(value.description)です.")
        .font(.title)
        .foregroundColor(value)
      Spacer().fixedSize()
      ColorPicker("カラーピッカー", selection: $value)
        .padding(25)
      Spacer()
    }
  }
}


リスト4-12
struct ContentView: View {
  @State var value: Date = Date()
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text("\(value)です.")
        .font(.title2)
      Spacer().fixedSize()
      DatePicker("日時を選択", selection: $value, 
          displayedComponents: [.date, .hourAndMinute])
        .padding(20)
      Spacer()
    }
  }
}


リスト4-13
func getDate(y: Int, m: Int, d: Int)->Date {
  let calendar = Calendar(identifier: .gregorian)
  let date = calendar.date(from: DateComponents(year: y, month: m, day: d, hour: 0, minute: 0, second: 0))
  return date ?? Date()
}
func getFmt()->DateFormatter {
  let fmt = DateFormatter()
  fmt.dateFormat = "yyyy年M月d日(E)"
  return fmt
}

struct ContentView: View {
  @State var value: Date = Date()
  let value0: Date = getDate(y: 2001, m: 1, d: 1)
  let fmt = getFmt()
  var dt: TimeInterval { value0.distance(to: value)}
  var dd: Int { Int(dt) / 60 / 60 / 24 }
  
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text("\(fmt.string(from: value0))から\(fmt.string(from: value))までの日数は、\(dd)日です。")
        .font(.title2)
      Spacer().fixedSize()
      DatePicker("日時を選択", selection: $value, displayedComponents: [.date])
        .padding(20)
      Spacer()
    }
  }
}


リスト4-14
struct ContentView: View {
  @State var name = ""
  @State var pass = ""
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text("名前：\(name)。パスワードは\(pass.count)文字です。")
        .padding(10).font(.title2)
      Form {
        TextField("Name", text: $name)
        SecureField("Pass", text: $pass)
      }
      Spacer()
    }
  }
}


リスト4-15
struct ContentView: View {
  @State var name = ""
  @State var pass = ""
  @State var msg = "Sign in: "
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text(msg)
        .padding(10).font(.title2)
      Form {
        Section(header: Text("Sign in")) {
          TextField("Name", text: $name)
          SecureField("Pass", text: $pass)
        }
        Section("Button", content: {
          Button("action") {
            msg = "Name: \(name). Pass: \(pass.count) chars."
          })
        })
      }
      Spacer()
    }
  }
}


リスト4-16
struct ContentView: View {
  @State var msg: String = "Hello."
  @State var flag: Bool = false
  @State var input: String = ""
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text(msg).font(.largeTitle)
      Spacer().fixedSize()
      TextField("your name", text: $input)
        .padding(10)
        .font(.title2)
        .textFieldStyle(RoundedBorderTextFieldStyle())
      Button(action: {
        flag = true
      }, label: {
        Text("action")
          .font(.title)
      })
        .alert("Hello, \(input)!", isPresented: $flag){
          Button("閉じる") {
            input = ""
          }
        }
      Spacer()
    }
  }
}


リスト4-17
struct ContentView: View {
  @State var msg: String = "Hello."
  @State var flag: Bool = false
  @State var input: String = ""
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text(msg).font(.largeTitle)
      Spacer().fixedSize()
      TextField("your name", text: $input)
        .padding(10)
        .font(.title2)
        .textFieldStyle(RoundedBorderTextFieldStyle())
      Button(action: {
        flag = true
      },label: {
        Text("action")
          .font(.title)
      }).confirmationDialog("ダイアログ",
          isPresented: $flag) {
        Button("了解") { msg = "\(input)を了解しました。" }
        Button("拒否") { msg = "\(input)は拒否しました。" }
      }
      Spacer()
    }
  }
}


リスト4-18
struct ContentView: View {
  @State var msg: String = "Hello."
  @State var flag: Bool = false
  @State var input: String = ""
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text(msg).font(.largeTitle)
      Spacer().fixedSize()
      Button(action: {
        flag = true
      },label: {
        Text("action")
          .font(.title)
      }).sheet(isPresented: $flag) {
        msg = "\(input)と入力。"
      } content: {
        Text("テキストを入力：").font(.title2)
        TextField("", text: $input).padding(20)
          .textFieldStyle(RoundedBorderTextFieldStyle())
        Button("閉じる") {
          flag = false
        }
      }
      Spacer()
    }
  }
}


リスト4-19
struct ContentView: View {
  @State var msg: String = "Hello."
  @State var flag: Bool = false
  @State var input: String = ""
  var body: some View {
    VStack {
      Spacer().fixedSize()
      Text(msg).font(.largeTitle)
      Spacer().fixedSize()
      Button(action: {
        flag = true
      },label: {
        Text("action")
          .font(.title)
      }).popover(isPresented: $flag) {
        Spacer().fixedSize()
        Text("Popover").font(.title)
        TextField("", text: $input).padding(20)
          .textFieldStyle(RoundedBorderTextFieldStyle())
        Button("閉じる") {
          msg = "入力：\(input)."
          flag = false
        }
        Spacer()
      } 
      Spacer()
    }
  }
}


リスト5-1
struct ContentView: View {
  var body: some View {
    TabView {
      HomeView()
        .tabItem {
          Label("Home", systemImage: "person")
        }

      StoryView()
        .tabItem {
          Label("Story", systemImage: "book")
        }
      
      FavoritesView()
        .tabItem {
          Label("Favorites", systemImage: "star")
        }
      
      FunFactsView()
        .tabItem {
          Label("Fun Facts", systemImage: "hand.thumbsup")
        }
    }
  }
}


リスト5-2
struct ContentView: View {
  var body: some View {
    TabView {
      HomeView()

      StoryView()
      
      FavoritesView()
      
      FunFactsView()
    }
  }
}


リスト5-3
struct ContentView: View {
  var body: some View {
    TabView {
      VStack {
        Text("First").font(.title)
        Text("これは最初のページです。").padding()
        Spacer()
      }.tabItem {
        Label("1st", systemImage: "star")
      }
      VStack {
        Text("Second").font(.title)
        Text("これは真ん中にあるページです。").padding()
        Spacer()
      }.tabItem {
        Label("2nd", systemImage: "star.circle")
      }
      VStack {
        Text("Third").font(.title)
        Text("これは、最後のページです。").padding()
        Spacer()
      }.tabItem {
        Label("3rd", systemImage: "moon.stars")
      }
    }
  }
}


リスト5-4
struct StoryView: View {

  var body: some View {
    NavigationView {
      StoryPageView(story: story, pageIndex: 0)
    }
    .navigationViewStyle(.stack)
  }
}


リスト5-5
struct StoryPageView: View {

  var body: some View {
    VStack {
      ScrollView {
        Text(……)
      }
      ForEach(……) { choice in
        NavigationLink(destination: ……) {……}
      }
    }
    .padding()
    .navigationTitle("Page \(pageIndex + 1)")
    .navigationBarTitleDisplayMode(.inline)
  }
}


リスト5-6
struct PageView: View {
  @State var page: Int
  @State var msg: String
  @State var color: Color
  
  var body: some View {
    VStack {
      Text("これは、\(page)ページ目です.").font(.title2)
        .foregroundColor(color)
      Text(msg).padding()
      Spacer()
    }.padding()
    .navigationTitle("Page \(page).")
    .navigationBarTitleDisplayMode(.inline)
  }
}


リスト5-7
struct ContentView: View {
  var body: some View {
    NavigationView {
      VStack {
        Text("First").font(.title)
        Text("これはスタートページです。").padding()
        Spacer()
        NavigationLink("first", destination: PageView(page: 1, msg: "ナビゲーションで表示された最初のコンテンツです。", color: Color.red))
          .font(.title2)
        NavigationLink("Second", destination: PageView(page: 2, msg: "ナビゲーションで表示された真ん中のコンテンツです。", color: Color.green))
          .font(.title2)
        NavigationLink("Third", destination: PageView(page: 3, msg: "ナビゲーションで表示された最後のコンテンツです。", color: Color.blue))
          .font(.title2)
        Spacer()
      }
    }
  }
}


リスト5-8
struct ContentView: View {
  var body: some View {
    NavigationView {
      VStack {
        Text("First").font(.title)
        Text("これはスタートページです。").padding()
        List {
          NavigationLink("first", 
            destination: PageView(page: 1, msg: "ナビゲーションで表示された最初のコンテンツです。", color: Color.red))
          .font(.title2)
          NavigationLink("Second", 
            destination: PageView(page: 2, msg: "ナビゲーションで表示された真ん中のコンテンツです。", color: Color.green))
          .font(.title2)
          NavigationLink("Third", 
            destination: PageView(page: 3, msg: "ナビゲーションで表示された最後のコンテンツです。", color: Color.blue))
          .font(.title2)
        }
        Spacer()
      }
    }
  }
}


リスト5-9
struct PageData: Identifiable {
  let id = UUID()
  var page: Int
  var msg: String
  var color: Color
  var icon: String
}


リスト5-10
struct PageItem: View {
  var data: PageData
  var body: some View {
    HStack {
      Image(systemName: data.icon)
        .resizable()
        .scaledToFit()
        .frame(width: 25.0, height: 25.0)
        .foregroundColor(data.color)
      VStack {
        HStack {
          Text("Page \(data.page)")
            .font(.title2)
          Spacer()
        }
        HStack {
          Text(data.msg)
          Spacer()
        }
      }
    }
  }
}


リスト5-11
let pagedata = [
  PageData(page: 1, msg: "サンプルで作った項目です。", color: .red, icon: "heart.fill"),
  PageData(page: 2, msg: "PageDataはリストの項目用のデータです。", color: .green, icon: "star.fill"),
  PageData(page: 3, msg: "PageItemは、リストに表示する項目のビューです。", color: .blue, icon: "house.fill")
]


リスト5-12
struct ContentView: View {
  var body: some View {
    VStack {
      Text("First").font(.title).padding()
      Text("これはスタートページです。").padding()
      List {
        ForEach(pagedata) { item in
          PageItem(data: item)
        }
      }
    }
  }
}


リスト5-13
struct PageView: View {
  var data: PageData
  
  var body: some View {
    VStack {
      Image(systemName: data.icon)
        .resizable()
        .scaledToFit()
        .frame(width: 75.0, height: 75.0)
        .foregroundColor(data.color)
      Text("\(data.page)ページ目です.").font(.title)
        .foregroundColor(data.color)
      Text(data.msg).font(.title3).padding()
      Spacer()
    }.padding()
      .navigationTitle("Page \(data.page).")
      .navigationBarTitleDisplayMode(.inline)
  }
}


リスト5-14
struct ContentView: View {
  var body: some View {
    NavigationView {
      VStack {
        Text("First").font(.title)
        Text("これはスタートページです。").padding()
        List {
          ForEach(pagedata) { item in
            NavigationLink(destination: PageView(data: item)) {
              PageItem(data: item)
            }
          }
        }
        Spacer()
      }
    }
  }
}


リスト5-15
import MapKit


リスト5-16
struct ContentView: View {
  @State var region = MKCoordinateRegion(
    center: CLLocationCoordinate2D(
      latitude: 35.71, longitude: 139.81),
    span: MKCoordinateSpan(
      latitudeDelta: 0.0005, longitudeDelta: 0.005))
  
  var body: some View {
    VStack {
      Spacer()
      Text("Map").font(.title)
      Map(coordinateRegion: $region)
      Text("copyright 2022.")
    }
  }
}


リスト5-17
struct ContentView: View {
  static let center = CLLocationCoordinate2D(
    latitude: 35.681, longitude: 139.767)
  static let span = MKCoordinateSpan(
    latitudeDelta: 0.01, longitudeDelta: 0.01)
  @State var region = MKCoordinateRegion(
    center: center,
    span: span)
  
  var body: some View {
    VStack {
      Spacer()
      Text("Map").font(.title)
      Map(coordinateRegion: $region)
      Button("copyright 2022.") {
        region.center = ContentView.center
        region.span = ContentView.span
      }.padding()
    }
  }
}


リスト5-18
struct AnnotationItem: Identifiable {
  var id = UUID()
  var coordinate: CLLocationCoordinate2D
}


リスト5-19
let annotationData = [
  AnnotationItem(coordinate: CLLocationCoordinate2D(
    latitude: 35.683, longitude: 139.764)),
  AnnotationItem(coordinate: CLLocationCoordinate2D(
    latitude: 35.68, longitude: 139.771)),
  AnnotationItem(coordinate: CLLocationCoordinate2D(
    latitude: 35.677, longitude: 139.764))
]


リスト5-20
struct ContentView: View {
  static let center = CLLocationCoordinate2D(
    latitude: 35.681, longitude: 139.767)
  static let span = MKCoordinateSpan(
    latitudeDelta: 0.02, longitudeDelta: 0.02)
  @State var region = MKCoordinateRegion(
    center: center,
    span: span)
  
  var body: some View {
    VStack {
      Spacer()
      Text("Map").font(.title)
      Map(coordinateRegion: $region,
        annotationItems: annotationData
      ) { annotation in
        MapPin(coordinate: annotation.coordinate,
          tint: .black)
      }
      
      Button("copyright 2022.") {
        region.center = ContentView.center
        region.span = ContentView.span
      }.padding()
    }
  }
}


リスト5-21
struct AnnotationItem: Identifiable {
  var id = UUID()
  var name: String
  var icon: String
  var coordinate: CLLocationCoordinate2D
}


リスト5-22
let annotationData = [
  AnnotationItem(name: "東京駅", icon: "pin.fill", 
    coordinate: CLLocationCoordinate2D(
      latitude: 35.681, longitude: 139.767))
]


リスト5-23
struct AnnotationView: View {
  var item: AnnotationItem
  var body: some View {
    VStack {
      Text(item.name)
        .font(.title)
        .padding()
        .background(.white)
      Image(systemName: item.icon)
        .resizable()
        .scaledToFit()
        .frame(width: 75.0, height: 75.0)
        .foregroundColor(.black)
    }
  }
}


リスト5-24
struct ContentView: View {
  static let center = CLLocationCoordinate2D(
    latitude: 35.681, longitude: 139.767)
  static let span = MKCoordinateSpan(
    latitudeDelta: 0.02, longitudeDelta: 0.02)
  @State var region = MKCoordinateRegion(
    center: center,
    span: span)
  
  var body: some View {
    VStack {
      Spacer()
      Text("Map").font(.title)
      Map(coordinateRegion: $region,
        annotationItems: annotationData,
        annotationContent: { annotation in
        MapAnnotation(coordinate: annotation.coordinate) {
          AnnotationView(item: annotation)
        }
      })
      Button("copyright 2022.") {
        region.center = ContentView.center
        region.span = ContentView.span
      }.padding()
    }
  }
}


リスト5-25
import WebKit


リスト5-26
struct WebView: UIViewRepresentable {
  var url: String
  
  func makeUIView(context: Context) -> WKWebView {
    return WKWebView()
  }
  
  func updateUIView(_ uiView: WKWebView, context: Context) {
    uiView.load(URLRequest(url: URL(string: url)!))
  }
}


リスト5-27
struct ContentView: View {
  @State var url = "https://www.apple.com/jp/"
  var body: some View {
    VStack {
      Spacer()
      Text("Web View").font(.title)
      TextField("", text: $url)
        .font(.title3)
        .textFieldStyle(RoundedBorderTextFieldStyle())
      WebView(url: url)
    }
  }
}


リスト5-28
struct WebView: UIViewRepresentable {
  var url: String
  let web: WKWebView = WKWebView()
  
  func makeUIView(context: Context) -> WKWebView {
    return web
  }
  
  func updateUIView(_ uiView: WKWebView, context: Context) {
    self.go(url: url)
  }
  
  func goback() {
    web.goBack()
  }
  func gonext() {
    web.goForward()
  }
  func go(url: String) {
    web.load(URLRequest(url: URL(string: url)!))
  }
}


リスト5-29
struct ContentView: View {
  static let url = "https://www.google.com/"
  private let web = WebView(url: ContentView.url)
  @State var input: String = ""
  
  var body: some View {
    VStack {
      Text("web").font(.title3)
      HStack {
        TextField("https: //", text: $input)
          .font(.title3)
          .textFieldStyle(RoundedBorderTextFieldStyle())
        Button("GO") {
          web.go(url: input)
        }.buttonStyle(.bordered)
      }
      web
      HStack {
        Group {
          Button("<<back") {
            web.goback()
          }
          Spacer()
          Button("Home") {
            web.go(url: ContentView.url)
          }
          Spacer()
          Button("next>>") {
            web.gonext()
          }
        }.font(.title2)
      }
    }
  }
}


リスト6-1
struct ContentView: View {
  var body: some View {
    ZStack {
      Rectangle()
        .fill(.red)
        .frame(width: 100, height: 100)
        .position(x: 100, y: 100)
      Rectangle()
        .stroke(lineWidth: 10)
        .foregroundColor(.yellow)
        .frame(width: 100, height: 100)
        .position(x: 150, y: 150)
    }
  }
}


リスト6-2
struct ContentView: View {
  var body: some View {
    ZStack {
      Rectangle()
        .fill(Color(red: 1.0, green: 0.9, blue: 0.9))
      Rectangle()
        .fill(.red)
        .frame(width: 100, height: 100)
        .position(x: 60, y: 60)
      Rectangle()
        .stroke(lineWidth: 10)
        .foregroundColor(.yellow)
        .frame(width: 100, height: 100)
        .position(x: 100, y: 100)
      VStack {
        Text("Shape sample")
          .font(.largeTitle)
          .padding(10)
        Text("Rectangle and Text view.")
          .font(.title2)
        Spacer()
      }
    }
  }
}


リスト6-3
struct ContentView: View {
  var body: some View {
    ZStack {
      RoundedRectangle(cornerSize: CGSize(width: 10,height: 10))
        .fill(.blue)
        .frame(width: 100, height: 100)
        .position(x: 100, y: 100)
      RoundedRectangle(cornerSize: CGSize(width: 25, height: 25))
        .stroke(lineWidth: 10)
        .foregroundColor(Color(red: 0, green: 1.0, blue: 1.0))
        .frame(width: 100, height: 100)
        .position(x: 150, y: 150)
    }
  }
}


リスト6-4
struct ContentView: View {
  var body: some View {
    ZStack {
      Circle()
        .fill(.blue)
        .frame(width: 100, height: 100)
        .position(x: 100, y: 100)
      Circle()
        .stroke(lineWidth: 10)
        .foregroundColor(Color(red: 0, green: 1.0, blue: 1.0))
        .frame(width: 100, height: 100)
        .position(x: 150, y: 150)
    }
  }
}


リスト6-5
struct ContentView: View {
  var body: some View {
    ZStack {
      Ellipse()
        .fill(.blue)
        .frame(width: 250, height: 100)
        .position(x: 150, y: 100)
      Ellipse()
        .stroke(lineWidth: 10)
        .foregroundColor(Color(red: 0, green: 1.0, blue: 1.0))
        .frame(width: 100, height: 150)
        .position(x: 200, y: 150)
    }
  }
}


リスト6-6
struct ContentView: View {
  var body: some View {
    ZStack {
      Capsule()
        .fill(.blue)
        .frame(width: 250, height: 100)
        .position(x: 150, y: 100)
      Capsule()
        .stroke(lineWidth: 10)
        .foregroundColor(Color(red: 0, green: 1.0, blue: 1.0))
        .frame(width: 100, height: 150)
        .position(x: 200, y: 150)
    }
  }
}


リスト6-7
struct ContentView: View {
  @State var count = 0
  var body: some View {
    ZStack {
      Capsule()
        .fill(.yellow)
        .frame(width: 250, height: 100)
        .position(x: 150, y: 100)
        .onTapGesture(count: 2) {
          count = 0
        }.onTapGesture {
          count += 1
        }
      Text("count: \(count).")
        .font(.largeTitle)
        .position(x: 150, y: 100)
    }
  }
}


リスト6-8
struct ContentView: View {
  @State var count = 0
  @State var position = CGPoint(x: 150, y: 100)
  var body: some View {
    ZStack {
      Capsule()
        .fill(.yellow)
        .frame(width: 250, height: 100)
        .position(position)
        .onTapGesture(count: 2) {
          count = 0
        }.onTapGesture {
          count += 1
        }
      Text("count: \(count).")
        .font(.largeTitle)
        .position(position)
    }.gesture(
      DragGesture(minimumDistance: 1)
        .onChanged {value in
          position = value.location
      })
  }
}


リスト6-9
struct ContentView: View {
  @State var msg = "tap me!"
  @State var input = ""
  @State var count = 0
  var body: some View {
    ZStack {
      Circle()
        .fill(.yellow)
        .frame(width: 100, height: 100)
        .position(x: 150, y: 200)
        .onTapGesture(count: 2) {
          count = 0
          msg = "reset now!"
        }.onTapGesture {
          count += 1
          msg = "count: \(count)."
        }.onDrag {
          NSItemProvider(object: "回数は、\(count)回です。" as NSString)
        }
      VStack {
        Text(msg).font(.title).padding(5)
        TextField("drop me...", text: $input)
          .font(.title2)
          .textFieldStyle(RoundedBorderTextFieldStyle())
        Spacer()
      }
    }
  }
}


リスト6-10
struct ContentView: View {
  @State var flag = true
  var body: some View {
    ZStack {
      Rectangle()
        .fill(.white)
        .onTapGesture {
          flag.toggle()
        }
      if flag {
        Circle()
          .fill(.yellow)
          .frame(width: 200, height: 200)
          .position(x: 150, y: 150)      }
    }
  }
}


リスト6-11
struct ContentView: View {
  @State var flag = true
  var body: some View {
    ZStack {
      Rectangle()
        .fill(.white)
        .onTapGesture {
          withAnimation {
            flag.toggle()
          }
        }
      Circle()
        .fill(.yellow)
        .frame(width: 200, height: 200)
        .position(x: 150, y: 150)
        .opacity(flag ? 1.0 : 0.0)
      Rectangle()
        .fill(.red)
        .frame(width: 200, height: 200)
        .position(x: 150, y: 150)
        .opacity(flag ? 0.0 : 1.0)
    }
  }
}


リスト6-12
struct ContentView: View {
  @State var flag = true
  var body: some View {
    ZStack {
      Rectangle()
        .fill(.white)
        .onTapGesture {
          withAnimation {
            flag.toggle()
          }
        }
      Circle()
        .fill(flag ? .yellow : .blue)
        .frame(width: flag ? 200 : 100, height: flag ? 200 : 100)
        .position(x: 150, y: 150)
      Rectangle()
        .fill(flag ? .red : .green)
        .frame(width: flag ? 50 : 200, height: flag ? 200 : 50)
        .position(x: flag ? 50 : 150, y: flag ? 150 : 50)
    }
  }
}


リスト6-13
struct ContentView: View {
  @State var flag = true
  var body: some View {
    ZStack {
      Rectangle()
        .fill(.white)
        .onTapGesture {
          withAnimation {
            flag.toggle()
          }
        }
      Circle()
        .offset(x: flag ? 0 : -100, y: flag ? 0 : 100)
        .fill(flag ? .yellow : .blue)
        .frame(width: 100, height: 100)
        .position(x: 200, y: 100)
      Rectangle()
        .rotation(Angle(degrees: flag ? 0: -90), anchor: .topTrailing)
        .fill(flag ? .red : .green)
        .frame(width: 50, height: 200)
        .position(x: 50, y: 150)
    }
  }
}


リスト6-14
struct ContentView: View {
  @State var flag = true
  var body: some View {
    ZStack {
      Rectangle()
        .fill(.white)
        .onTapGesture {
          withAnimation {
            flag.toggle()
          }
        }
      RotatedShape(shape: Rectangle(), angle: .degrees(flag ? 0: 405))
        .fill(.cyan)
        .frame(width: 100, height: 100)
        .position(x: 100, y: 100)
      OffsetShape(
        shape: ScaledShape(
          shape: Circle(), 
          scale: CGSize(width: flag ? 1 : 1.5, height: flag ? 1 : 1.5)),
        offset: CGSize(width: flag ? 0 : 100, height: 0))
        .fill(.red)
        .frame(width: 100, height: 100)
        .position(x: 100, y: 200)
    }
  }
}


リスト6-15
withAnimation(.easeInOut(duration: 5.0)) {
  flag.toggle()
}


リスト6-16
withAnimation(.spring(dampingFraction: 0.5)) {
  flag.toggle()
}


リスト6-17
struct ContentView: View {
  @State var degree = 0.6
  @State var count = 100.0
  var body: some View {
    VStack {
      Canvas { context, size in
        let p1 = Path(CGRect(x: 25, y: 25, width: 125, height: 125))
        context.fill(p1, with: .color(.blue))
        let p2 = Path(CGRect(x: 75, y: 75, width: 125, height: 125)) 
        context.stroke(p2, with: .color(.yellow), lineWidth: 10)
      }
      .frame(width: 300, height: 300)
      .position(x: 150, y: 150)
    }
  }
}


リスト6-18
struct ContentView: View {
  @State var degree = 0.6
  @State var count = 100.0
  var body: some View {
    VStack {
      Canvas { context, size in
        let p1 = Path(ellipseIn: CGRect(origin: CGPoint(x: 25, y: 25), 
          size: CGSize(width: 125, height: 125)))
        context.fill(p1, with: .color(.blue))
        let p2 = Path(ellipseIn: CGRect(x: 75, y: 75, width: 125, height: 125)) 
        context.stroke(p2, with: .color(.yellow), lineWidth: 10)
      }
      .frame(width: 300, height: 300)
      .position(x: 150, y: 150)
    }
  }
}


リスト6-19
struct ContentView: View {
  @State var degree = 0.6
  @State var count = 100.0
  var body: some View {
    VStack {
      Canvas { context, size in
        let p1 = Path(roundedRect: CGRect(origin: CGPoint(x: 25, y: 25), 
          size: CGSize(width: 125, height: 125)), cornerRadius: 25.0)
        context.fill(p1, with: .color(.blue))
        let p2 = Path(roundedRect: CGRect(x: 75, y: 75, width: 125, height: 125),
          cornerSize: CGSize(width: 25, height: 50)) 
        context.stroke(p2, with: .color(.yellow), lineWidth: 10)
      }
      .frame(width: 300, height: 300)
      .position(x: 150, y: 150)
    }
  }
}


リスト6-20
struct ContentView: View {
  @State var degree = 0.6
  @State var count = 100.0
  var body: some View {
    VStack {
      Slider(value: $degree, in: 0...1.0, step: 0.05)
      Slider(value: $count, in: 3...100, step: 1)
      Canvas { context, size in
          let dn = Double.pi * degree
          let center = CGPoint(x: 150, y: 150)
          var path = Path()
          for n in 0...Int(count) {
            let x = sin(dn * Double(n)) * 100.0 + center.x
            let y = cos(dn * Double(n)) * 100 + center.y
            if n == 0 {
              path.move(to: CGPoint(x: x, y: y))
            } else {
              path.addLine(to: CGPoint(x: x, y: y))
            }
          }
          context.stroke(path, with: .color(Color.blue), lineWidth: 2.0)
        }
    }
  }
}


リスト6-21
struct ContentView: View {
  @State var degree = 30.0
  @State var count = 50.0
  var body: some View {
    VStack {
      Slider(value: $degree, in: 0...360, step: 1)
      Slider(value: $count, in: 1...50, step: 1)
      Canvas { context, size in
        let dn = Double.pi / 360 * degree
        var path = Path()
        let r = CGRect(x: 0, y: 0, width: 40, height: 100)
        for n in 0...Int(count) {
          var affin = CGAffineTransform(translationX: 150, y: 150)
          affin = affin.rotated(by: dn * CGFloat(n))
          path.addRect(r, transform: affin)
        }
        context.stroke(path, with: .color(Color(red: 1.0, green: 0, blue: 0, opacity: 0.25)), lineWidth: 2.0)
      }
    }
  }
}


リスト6-22
struct ZukeiView: View {
  @State var degree = 30.0
  @State var count = 50.0
  @State var color = Color(red: 1.0, green: 0, blue: 0, opacity: 0.25)
  var body: some View {
    VStack {
      Slider(value: $degree, in: 0...360, step: 1)
      Slider(value: $count, in: 1...50, step: 1)
      Canvas { context, size in
        let w = size.width > size.height ? size.height : size.width
        let dn = Double.pi / 360 * degree
        var path = Path()
        let r = CGRect(x: 0, y: 0, width: w * 0.35, height: w * 0.35)
        for n in 0...Int(count) {
          var affin = CGAffineTransform(translationX: size.width * 0.5, y: size.height * 0.5)
          affin = affin.rotated(by: dn * CGFloat(n))
          path.addRect(r, transform: affin)
        }
        context.stroke(path, with: .color(color), lineWidth: 2.0)
      }
    }
  }
}


リスト6-23
struct ContentView: View {
  var body: some View {
    VStack {
      HStack {
        ZukeiView()
        ZukeiView(color: .blue)
      }
      HStack {
        ZukeiView(color: .green)
        ZukeiView(color: .black)
      }
    }
  }
}


リスト7-1
import SpriteKit

class GameScene: SKScene {
}


リスト7-2
struct ContentView: View {
  var scene: SKScene {
    let scene = GameScene()
    scene.size = CGSize(width: 300, height: 300)
    scene.scaleMode = .fill
    return scene
  }
  
  var body: some View {
    VStack {
      Text("SKScene").font(.title2)
      SpriteView(scene: scene)
        .frame(width: 300, height: 300)
      Spacer()
    }
  }
}


リスト7-3
class GameScene: SKScene {
  var pos = CGPoint(x: 150, y: 150)
  var box: SKSpriteNode?
  
  func setup() {
    box = SKSpriteNode(color: .cyan, size: CGSize(width: 50, height: 50))
    box!.position = pos
    addChild(box!)
  }
  
  override func sceneDidLoad() {
    setup()
  }
}


リスト7-4
class GameScene: SKScene {
  var pos = CGPoint(x: 150, y: 150)
  var box: SKSpriteNode?
  
  func setup() {
    if box == nil {
      box = SKSpriteNode(imageNamed: "chara_image") //☆
      box!.position = pos
      box!.size = CGSize(width: 50, height: 50)
      addChild(box!)
    }
  }
  
  override func sceneDidLoad() {
    setup()
  }
}


リスト7-5
func makeSprite(name: String = "heart.fill", 
    color: UIColor = .red, 
    pos: CGPoint = CGPoint(x: 150, y: 150))->SKSpriteNode {
  let symbol = UIImage(systemName: name)!
    .withTintColor(color)
  let data = symbol.pngData()!
  let image = UIImage(data: data)! 
  let texture = SKTexture(image: image)
  let sprite = SKSpriteNode(texture: texture, color: color, 
    size: CGSize(width: 50, height: 50))
  sprite.position = pos
  return sprite
}


リスト7-6 
class GameScene: SKScene {
  var pos = CGPoint(x: 150, y: 150)
  var sprite: SKSpriteNode?
  
  override func sceneDidLoad() {
    sprite = makeSprite()
    addChild(sprite!)
  }
}


リスト7-7
class GameScene: SKScene {
  var pos = CGPoint(x: 150, y: 150)
  var d = CGSize(width: 1.0, height: 1.1)
  var sprite: SKSpriteNode?
  
  override func sceneDidLoad() {
    sprite = makeSprite(color: .yellow)
    addChild(sprite!)
  }
  
  override func update(_ currentTime: TimeInterval) {
    pos.x += d.width
    pos.y += d.height
    if pos.x <= 0 || pos.x >= 300{ d.width *= -1 }
    if pos.y <= 0 || pos.y >= 300 { d.height *= -1}
    sprite?.position = pos
  }
}


リスト7-8
class GameScene: SKScene {
  var pos = CGPoint(x: 150, y: 150)
  var sprite: SKSpriteNode?
  
  override func sceneDidLoad() {
    sprite = makeSprite(color: .yellow)
    addChild(sprite!)
    move()
  }

  func move() {
    sprite?.run(SKAction.moveBy(x: 100, y: 100, duration: 1.0))
  }
}


リスト7-9
func move() {
  let act = SKAction.sequence ([
    SKAction.moveBy(x: 100, y: 100, duration: 1.0),
    SKAction.moveBy(x: -100, y: -100, duration: 1.0)
  ])
  sprite?.run(act)
}


リスト7-10
class GameScene: SKScene {
  var pos = CGPoint(x: 150, y: 150)
  var sprite: SKSpriteNode?
  
  override func sceneDidLoad() {
    sprite = makeSprite(color: .yellow)
    addChild(sprite!)
  }

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first
    if touch == nil { return }
    let loc = touch!.location(in: self)
    let group1 = SKAction.group([
      SKAction.moveTo(x: loc.x, duration: 1.0),
      SKAction.moveTo(y: loc.y, duration: 1.0)
    ])
    sprite?.run(group1)
  }
}


リスト7-11
class GameScene: SKScene {
  var pos = CGPoint(x: 150, y: 150)
  
  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first
    if touch == nil { return }
    let loc = touch!.location(in: self)
    let sprite = makeSprite(color: .orange, pos: loc)
    addChild(sprite)
  }
}


リスト7-12
class SampleNode : SKSpriteNode {
  var d = CGSize(width: CGFloat.random(in: -1.0...1.0),
    height: CGFloat.random(in: -1.0...1.0))
  
  public func update() {
    var pos = position
    pos.x += d.width
    pos.y += d.height
    if pos.x <= 0 || pos.x >= 300{ d.width *= -1 }
    if pos.y <= 0 || pos.y >= 300 { d.height *= -1}
    position = pos
  }
}


リスト7-13
let sprite = SampleNode(texture: texture, color: color, 
    size: CGSize(width: 50, height: 50))


リスト7-14
class GameScene: SKScene {
  let colors: [UIColor] = [.red, .blue, .green, .yellow, .orange, 
      .cyan, .brown, .purple, .gray]
  
  override func update(_ currentTime: TimeInterval) {
    for sp in children {
      let node = sp as? SampleNode
      node?.update()
    }
  }

  override func touchesBegan(_ touches: Set<UITouch>, 
      with event: UIEvent?) {
    let touch = touches.first
    if touch == nil { return }
    let loc = touch!.location(in: self)
    for sp in children {
      if sp.contains(loc) {
        sp.run(SKAction.fadeOut(withDuration: 2)) {   
          sp.removeFromParent()
        }
        return
      }
    }
    let c: UIColor = colors.randomElement()!
    let sprite = makeSprite(color: c, pos: loc)
    addChild(sprite)
  }
}


リスト7-15
class GameScene: SKScene {
  let colors: [UIColor] = [.red, .blue, .green, .yellow, .orange, 
      .cyan, .brown, .purple, .gray]
  let wh = CGSize(width: 50, height: 50)
  
  override func didMove(to view: SKView) {
    physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
  }
  
  override func touchesBegan(_ touches: Set<UITouch>, 
      with event: UIEvent?) {
    let touch = touches.first
    if touch == nil { return }
    let loc = touch!.location(in: self)
    let box = SKSpriteNode(color: colors.randomElement()!, size: wh)
    box.position = loc
    box.physicsBody = SKPhysicsBody(rectangleOf: wh)
    addChild(box)
    if children.count >= 20 { // max 20 pieces.
      children.first?.removeFromParent()
    }
  }
}


リスト7-16
class PhysicsNode : SKSpriteNode {
  public var count = 0.0
  let max = 10.0
  
  public func collide() {
    count += 1.0
    alpha = 1.0 - (count / max)
    if count >= max {
      removeFromParent()
    }
  }
}


リスト7-17
class GameScene: SKScene, SKPhysicsContactDelegate {
  let colors: [UIColor] = [.red, .blue, .green, .yellow, .orange, 
    .cyan, .brown, .purple, .gray]
  let wh = CGSize(width: 50, height: 50)
  var num = 0
  let category = [0b0001, 0b0010, 0b0011]
  
  override func didMove(to view: SKView) {
    physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
    physicsBody?.categoryBitMask = 0b0001
    physicsBody?.collisionBitMask = 0b0001
    physicsBody?.contactTestBitMask = 0b0001
    physicsWorld.contactDelegate = self
    self.backgroundColor = .darkGray
  }
  
  override func touchesBegan(_ touches: Set<UITouch>, 
      with event: UIEvent?) {
    let touch = touches.first
    if touch == nil { return }
    let loc = touch!.location(in: self)
    let box = PhysicsNode(color: colors.randomElement()!, size: wh)
    num += 1
    box.name = "No, \(num)"
    box.position = loc
    box.physicsBody = SKPhysicsBody(rectangleOf: wh)
    box.physicsBody?.categoryBitMask = 0b0010
    box.physicsBody?.collisionBitMask = 0b0011
    box.physicsBody?.contactTestBitMask = 0b0010
    addChild(box)
  }
  
  func didBegin(_ contact: SKPhysicsContact) {
    if contact.bodyA.contactTestBitMask != 0b0010 || 
      contact.bodyB.contactTestBitMask != 0b0010 {
      return
    }
    let objA = contact.bodyA.node as? PhysicsNode
    let objB = contact.bodyB.node as? PhysicsNode
    objA?.collide()
    objB?.collide()
  }
  
  func didEnd(_ contact: SKPhysicsContact) {
    print(" \(contact.bodyA.categoryBitMask)-\(contact.bodyB.collisionBitMask).")
  }
}


リスト7-18
func touchesBegan(_ touches: Set<UITouch>, 
    with event: UIEvent?) {
  let touch = touches.first
  if touch == nil { return }
  let loc = touch!.location(in: self)
  let box: SKShapeNode
  switch (1...3).randomElement() {
  case 1: 
    box = SKShapeNode(rect: CGRect(x: -25, y: -25, width: 50, height: 50))
    box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, height: 50))
    box.physicsBody?.categoryBitMask = 0b0010
    box.physicsBody?.collisionBitMask = 0b0111
    box.physicsBody?.contactTestBitMask = 0b0010
  case 2: 
    box = SKShapeNode(rect: CGRect(x: -12.5, y: -37.5, width: 25, height: 75))
    box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 25, height: 75))
    box.physicsBody?.categoryBitMask = 0b0100
    box.physicsBody?.collisionBitMask = 0b0111
    box.physicsBody?.contactTestBitMask = 0b0100
  default: 
    box = SKShapeNode(circleOfRadius: 25.0)
    box.physicsBody = SKPhysicsBody(circleOfRadius: 25.0)
    box.physicsBody?.categoryBitMask = 0b1000
    box.physicsBody?.collisionBitMask = 0b1001
    box.physicsBody?.contactTestBitMask = 0b1000
  }
  box.fillColor = colors.randomElement()!
  num += 1
  box.name = "No, \(num)"
  box.position = loc
  addChild(box)
}


リスト7-19
class GameScene: SKScene, SKPhysicsContactDelegate {
  let colors: [UIColor] = [.red, .blue, .green, .yellow, .orange, 
    .cyan, .brown, .purple, .gray]
  let wh = CGSize(width: 50, height: 50)
  var num = 0
  let max = 0
  let category = [0b0001, 0b0010, 0b0011]
  var msg: SKLabelNode?
  
  override func didMove(to view: SKView) {
    physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
    physicsBody?.categoryBitMask = 0b0001
    physicsBody?.collisionBitMask = 0b0001
    physicsBody?.contactTestBitMask = 0b0001
    physicsWorld.contactDelegate = self
    msg = SKLabelNode(text: "Start!")
    msg!.fontSize = 20
    msg!.fontColor = .white
    msg!.fontName = "Thonburi-Bold"
    msg!.position = CGPoint(x: 10, y: frame.height - 35)
    msg!.horizontalAlignmentMode = .left
    addChild(msg!)
    self.backgroundColor = .darkGray
  }
  
  override func touchesBegan(_ touches: Set<UITouch>, 
      with event: UIEvent?) {
    let touch = touches.first
    if touch == nil { return }
    let loc = touch!.location(in: self)
    let box = SKShapeNode(circleOfRadius: 25.0)
    box.physicsBody = SKPhysicsBody(circleOfRadius: 25.0)
    box.physicsBody?.categoryBitMask = 0b1000
    box.physicsBody?.collisionBitMask = 0b1001
    box.physicsBody?.contactTestBitMask = 0b1000
    box.fillColor = colors.randomElement()!
    num += 1
    box.name = "No, \(num)"
    box.position = loc
    box.userData = NSMutableDictionary()
    box.userData?.setValue(0, forKey: "count")
    addChild(box)
    msg?.text = "Add No, \(num)!"
  }
  
  func didBegin(_ contact: SKPhysicsContact) {
    if contact.bodyA.contactTestBitMask != 0b1000 || 
      contact.bodyB.contactTestBitMask != 0b1000 {
      return
    }
    let valA = contact.bodyA.node?.userData?.value(forKey: "count") as! Int
    let valB = contact.bodyB.node?.userData?.value(forKey: "count") as! Int
    if valA >= max {
      contact.bodyA.node?.removeFromParent()
    }
    if valB >= max {
      contact.bodyB.node?.removeFromParent()
    }
    contact.bodyA.node?.userData?.setValue(valA + 1, forKey: "count")
    contact.bodyB.node?.userData?.setValue(valB + 1, forKey: "count")
  }
  
  func didEnd(_ contact: SKPhysicsContact) {
  }
}


リスト7-20
class GameScene: SKScene, SKPhysicsContactDelegate {
  let colors: [UIColor] = [.red, .blue, .green, .yellow, .orange, 
                .cyan, .brown, .purple, .gray]
  let wh = CGSize(width: 50, height: 50)
  var msg: SKLabelNode?
  var gameLayer: SKNode?
  var labelLayer: SKNode?
  var lastTime: TimeInterval = 0
  var timeInterval = 10.0
  var flag = false
  var scW: CGFloat = 300
  var scH: CGFloat = 300
  var currentNode: SKNode? = nil
  var score = 0
  var count = 0
  
  // ゲームの初期化
  func setup() {
    scW = frame.width
    scH = frame.height
    physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
    physicsBody!.categoryBitMask = 0b0001
    physicsBody!.collisionBitMask = 0b0001
    physicsBody!.contactTestBitMask = 0b0001
    physicsWorld.contactDelegate = self
    gameLayer = SKNode()
    addChild(gameLayer!)
    labelLayer = SKNode()
    addChild(labelLayer!)
    msg = SKLabelNode(text: "Start!")
    msg!.fontSize = 20
    msg!.fontColor = .white
    msg!.fontName = "Thonburi-Bold"
    msg!.position = CGPoint(x: 10, y: scH - 35)
    msg!.horizontalAlignmentMode = .left
    labelLayer!.addChild(msg!)
    self.backgroundColor = .darkGray
  }
  
   // ゲーム開始
   func start() {
    score = 0
    count = 0
    timeInterval = 10
    physicsWorld.gravity = CGVector(dx: 0, dy: -0.1)
    currentNode = nil
    lastTime = 0
    gameLayer?.removeAllChildren()
    flag = true
  }
  
  // ノード作成
  func makeNode() {
    if currentNode != nil && currentNode!.position.y > scH - 50 {
      flag = false
      msg?.text = "GAME OVER. [\(score)]"
      return
    }
    let box = PhysicsNode(color: colors.randomElement()!, size: wh)
    box.position = CGPoint(x: CGFloat.random(in: 50...scW) - 25, y: scH - 25)
    box.physicsBody = SKPhysicsBody(rectangleOf: wh)
    box.physicsBody!.categoryBitMask = 0b1000
    box.physicsBody!.collisionBitMask = 0b1001
    box.physicsBody!.contactTestBitMask = 0b1000
    gameLayer!.addChild(box)
    currentNode = box
    count += 1
    score += 10
    msg?.text = String(score)
    if count >= 10  {
      count = 0
      physicsWorld.gravity.dy -= 0.1
      timeInterval -= 1.0
      if timeInterval < 1.0 { timeInterval = 1.0 }
    }
  }

  // ノード消去のチェック  
  func check() {
    if currentNode == nil { return }
    let y = currentNode!.position.y
    let area = CGRect(x: 0, y: y - 25, width: scW, height: 50)
    var list: [SKNode] = []
    for item in gameLayer!.children {
      if area.contains(item.position) {
        list.append(item)
      }
    }
    if list.count >= Int(scW / 50) - 1 {
      score += list.count * 100
      msg?.text = String(score)
      for item in list {
        item.removeFromParent()
      }
    }
  }
  
  // シーンの開始
   override func didMove(to view: SKView) {
    setup()
  }
  
  // フレーム更新
  override func update(_ currentTime: TimeInterval) {
    if !flag { return }
    if lastTime + timeInterval < currentTime {
      lastTime = currentTime
      makeNode()
    }
    check()
  }
  
  // 画面タップ
  override func touchesBegan(_ touches: Set<UITouch>, 
      with event: UIEvent?) {
    if !flag {
      flag = true
      start()
      return
    }
    let touch = touches.first
    if touch == nil { return }
    let loc = touch!.location(in: self)
    if loc.x > frame.width / 2 {
      currentNode?.position.x += 5
    } else {
      currentNode?.position.x -= 5
    }
  }
}


リスト8-1
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
  @Binding var flag: Bool
  @Binding var image: Image?
  
  init(flag: Binding<Bool>, image: Binding<Image?>) {
    _flag = flag
    _image = image
  }
  
  func imagePickerController(_ picker: UIImagePickerController, 
      didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    let img = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
    image = Image(uiImage: img)
    flag = false
  }
  
  func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    flag = false
  }
}


リスト8-2
struct CameraView: UIViewControllerRepresentable {
  @Binding var flag: Bool
  @Binding var image: Image?
  
  func updateUIViewController(_ uiViewController: UIImagePickerController, 
    context: UIViewControllerRepresentableContext<CameraView>) {
  }
  
  func makeCoordinator() -> Coordinator {
    Coordinator(flag: $flag, image: $image)
  }
  
  func makeUIViewController(context: UIViewControllerRepresentableContext<CameraView>) 
  -> UIImagePickerController {
    let controller = UIImagePickerController()
    controller.delegate = context.coordinator
    controller.sourceType = .camera
    return controller
  }
}


リスト8-3
struct ContentView: View {
  @State var flag = false
  @State var image: Image?  = Image(systemName: "camera")
  
  var body: some View {
    VStack {
      Text("Photo").font(.title).padding()
      image?.resizable().scaledToFit()
        .frame(width: 300, height: 300)
        .foregroundColor(.blue)
        .background(Color(red: 0.8, green: 0.9, blue: 1.0))
      Button("Click!") {
        flag = true
      }.font(.title2)
      Spacer()
    }.sheet(isPresented: $flag) {
      CameraView(flag: $flag, image: $image)
    }
  }
}


リスト8-4
class LocationManager: NSObject, ObservableObject, 
    CLLocationManagerDelegate {
  let manager = CLLocationManager()
  @Published var loc = CLLocation()
  
  override init() {
    super.init()
    manager.requestWhenInUseAuthorization()
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.distanceFilter = 1
    manager.delegate = self
    manager.startUpdatingLocation()
  }
  
  func locationManager(_ manager: CLLocationManager, 
      didUpdateLocations locations: [CLLocation]) {
    loc = locations[0]
  }
}


リスト8-5
struct ContentView: View {
  @ObservedObject var manager = LocationManager()
  
  var body: some View {
    VStack {
      Text("GPS").font(.title).padding()
      Text("LAT: \(manager.loc.coordinate.latitude).")
        .font(.title2)
      Text("LNG: \(manager.loc.coordinate.longitude).")
        .font(.title2)
      Spacer()
    }
  }
}


リスト8-6
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
  let manager = CLLocationManager()
  @Published var loc = CLLocation()
  @Published var heading: CLLocationDirection = 0.0 //☆
  
  override init() {
    super.init()
    manager.requestWhenInUseAuthorization()
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.distanceFilter = 1
    manager.delegate = self
    manager.startUpdatingLocation()
    manager.headingFilter = kCLHeadingFilterNone //☆
    manager.headingOrientation = .portrait //☆
    manager.startUpdatingHeading() //☆
  }
  
  func locationManager(_ manager: CLLocationManager, 
      didUpdateLocations locations: [CLLocation]) {
    loc = locations[0]
  }
  
  // ☆追加メソッド
  func locationManager(_ manager: CLLocationManager, 
      didUpdateHeading heading: CLHeading) {
    self.heading = heading.magneticHeading
  }
}


リスト8-7
struct ContentView: View {
  @ObservedObject var manager = LocationManager()
  
  var body: some View {
    VStack {
      Text("Compass").font(.title).padding()
      Text("Value: \(manager.heading).")
        .font(.title2)
      Image(systemName: "location.north")
        .resizable().scaledToFit()
        .foregroundColor(.blue)
        .frame(width: 100, height: 100)
        .padding()
        .rotationEffect(Angle.degrees(360 - manager.heading))
      Spacer()
    }
  }
}


リスト8-8
struct ContentView: View {
  let motion = CMMotionManager()
  @State var flag = false
  @State var circleX = 150.0
  @State var circleY = 150.0
  
  var body: some View {
    VStack {
      Text("Accel").font(.title).padding()
      Button("Start!") {
        if flag {
          motion.stopDeviceMotionUpdates()
          print("stop!")
        } else {
          motion.accelerometerUpdateInterval = 0.025
          motion.startAccelerometerUpdates(to: OperationQueue.current!) { 
          (accel: CMAccelerometerData?, err: Error?) in
            update(data: accel!)
          }
        }
        flag = !flag
      }
      ZStack {
        Rectangle()
          .fill(Color.yellow)
          .frame(width: 300, height: 300)
        Circle()
          .fill(Color.red)
          .frame(width: 50, height: 50)
          .position(x: circleX, y: circleY)
      }.frame(width: 300, height: 300)
      Spacer()
    }
  }
  func update(data: CMAccelerometerData) {
    let x0 = data.acceleration.x
    let y0 = data.acceleration.y
    var x = Double(x0) * 200
    var y = Double(y0) * -200
    if x < -150 { x = -150 }
    if x > 150 { x = 150 }
    if y < -150 { y = -150 }
    if y > 150 { y = 150 }
    circleX = x + 150
    circleY = y + 150
  }
}


リスト8-9
class GameScene: SKScene {
  let motion = CMMotionManager()
  var sprite: SKShapeNode?
  
  override func didMove(to view: SKView) {
    backgroundColor = UIColor(Color(red: 0.8, green: 0.9, blue: 1.0))
    physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
    physicsWorld.gravity = CGVector(dx: 0, dy: 0)
    sprite = SKShapeNode(circleOfRadius: 25.0)
    sprite!.physicsBody = SKPhysicsBody(circleOfRadius: 25.0)
    sprite!.physicsBody!.categoryBitMask = 0b1000
    sprite!.physicsBody!.collisionBitMask = 0b1001
    sprite!.physicsBody!.contactTestBitMask = 0b1000
    sprite!.fillColor = .red
    sprite!.position = CGPoint(x: 150, y: 150)
    addChild(sprite!)
    // ☆motion setting.
    motion.accelerometerUpdateInterval = 0.025
    motion.startGyroUpdates(to: OperationQueue.current!) { 
      (data: CMGyroData?, err: Error?) in
      let x0 = data?.rotationRate.y
      let y0 = data?.rotationRate.x
      var x = Int(Double(x0!) * 10)
      var y = Int(Double(y0!) * -10)
      if x < -150 { x = -150 }
      if x > 150 { x = 150 }
      if y < -150 { y = -150 }
      if y > 150 { y = 150 }
      let nx = x
      let ny = y
      self.sprite!.physicsBody!.applyForce(CGVector(dx: nx, dy: ny))
    }
  }
}


リスト8-10
struct ContentView: View {
  let motion = CMMotionManager()
  var scene: SKScene {
    let scene = GameScene()
    scene.size = CGSize(width: 300, height: 300)
    scene.scaleMode = .fill
    return scene
  }
  
  var body: some View {
    VStack {
      Text("Gyro").font(.title).padding()
      SpriteView(scene: scene)
        .frame(width: 300, height: 300)
      Spacer()
    }
  }
}


