iOS.& Swift Books iOS.Apprentice

46
抛光靶心 由Joey Devilla撰写

靶心 作品!该游戏元素是完整的。正如在前面的章节答应了,你现在要使它看起来很漂亮。 SwiftUI使这相当容易。

您还可以做一些重构。有一个在代码中的一些改进的余地,其结果将是代码更容易既理解和维护。

在本章中,你将涵盖以下内容:

  • 加剧图形: 您将学习SWIFTUI方法以防止默认出现的视图,甚至可以创建可重复使用的样式。
  • “关于”屏幕: 造型后 靶心的主屏幕上,你会解决的‘关于’屏幕。
  • 一些最终触摸: 一旦你制作了 靶心 更好的,你会增加一些触摸。总是有改进的空间!

加剧图形

摆脱状态栏只是第一步。我们想从这个......

该应用程序现在如何看待
该应用程序现在如何看待

......到这一点:

该应用程序将如何看待结束
该应用程序将如何看待结束

在对应用程序的外观进行这些更改时,您将添加图像到视图,甚至在现有视图中添加其他视图。如果您已经完成了一些HTML设计,您会发现您要做的很多事情即将熟悉。

添加图像资产

像UIKit的项目,SwiftUI使用存储在良好的醇资产“ assets.xcassets.。让我们添加 靶心 图像到项目。

将文件拖入资产目录中
XLUFQEVC Forin Ulhu YPU Uzkif Komakom

图像现在在资产目录中
rko esureb ali hok eyjonu kyu avcif qatamol

把壁纸拿起

让我们首先取代 靶心的单调的白色背景更吸引人 背景 像您添加到应用程序的资产目录:

背景图像
PTI Cerkrfaibp Ozuqu

var body: some View {
  VStack {
    Spacer()

    // Target row
...
    // Score row
    HStack {
      Button(action: {
        self.startNewGame()
      }) {
        Text("重来")
      }
      Spacer()
      Text("Score:")
      Text("\(self.score)")
      Spacer()
      Text("Round:")
      Text("\(self.round)")
      Spacer()
      NavigationLink(destination: AboutView()) {
        Text(" 信息 ")
      }
    }
    .padding(.bottom, 20)
  }
  .background(Image("背景"))
}
iPhone 8上的2倍背景
QWI 5K MOYPFBUOQQ EK CBA Alzoyo 3

改变文本

现在 靶心 有新的背景图像,黑色的文字是现在几乎难以辨认。我们需要改变它,以便更好脱颖而出。再次,said'll使用一些内置的方法来改变文字的外观,以便它的映衬下清晰可辨。让我们先从“把靶心尽可能靠近你可以:”和目标值的文本。

// Target row
HStack {
  Text("Put the bullseye as close as you can to:")
    .font(Font.custom("Arial Rounded MT Bold", size: 18))
    .foregroundColor(Color.white)
    .shadow(color: Color.black, radius: 5, x: 2, y: 2)
  Text("\(target)")
    .font(Font.custom("Arial Rounded MT Bold", size: 24))
    .foregroundColor(Color.yellow)
    .shadow(color: Color.black, radius: 5, x: 2, y: 2)
}
目标行,带有风格的文本
Sli Bongid Wor,Poqb Pygam xazr

// Slider row
HStack {
  Text("1")
    .font(Font.custom("Arial Rounded MT Bold", size: 18))
    .foregroundColor(Color.white)
    .shadow(color: Color.black, radius: 5, x: 2, y: 2)
  Slider(value: $sliderValue, in: 1...100)
  Text("100")
    .font(Font.custom("Arial Rounded MT Bold", size: 18))
    .foregroundColor(Color.white)
    .shadow(color: Color.black, radius: 5, x: 2, y: 2)
}
目标和滑块排,样式文本
YZU ruxqiw OHR yvotin femd,navk mhpwic giny

// Score row
HStack {
  Button(action: {
    self.startNewGame()
  }) {
    Text("重来")
  }
  Spacer()
  Text("Score:")
    .font(Font.custom("Arial Rounded MT Bold", size: 18))
    .foregroundColor(Color.white)
    .shadow(color: Color.black, radius: 5, x: 2, y: 2)
  Text("\(score)")
    .font(Font.custom("Arial Rounded MT Bold", size: 24))
    .foregroundColor(Color.yellow)
    .shadow(color: Color.black, radius: 5, x: 2, y: 2)
  Spacer()
  Text("Round")
    .font(Font.custom("Arial Rounded MT Bold", size: 18))
    .foregroundColor(Color.white)
    .shadow(color: Color.black, radius: 5, x: 2, y: 2)
  Text("\(round)")
    .font(Font.custom("Arial Rounded MT Bold", size: 24))
    .foregroundColor(Color.yellow)
    .shadow(color: Color.black, radius: 5, x: 2, y: 2)
  Spacer()
  NavigationLink(destination: AboutView()) {
    Text(" 信息 ")
  }
}
.padding(.bottom, 20)
应用程序,所有文本都样式
LLO URK,NEQR OFD EBH Volq FPPNUD

使按钮看起来像按钮

让我们的按钮看起来更像按钮。

按钮映像
Qla Rohzus Odubo.

// Button row
Button(action: {
  print("Button pressed!")
  self.alertIsVisible = true
}) {
  Text("Hit me!")
  .font(Font.custom("Arial Rounded MT Bold", size: 18))
  .foregroundColor(Color.black)
}
.background(Image("Button")
  .shadow(color: Color.black, radius: 5, x: 2, y: 2)
)
.alert(isPresented: $alertIsVisible) {
  Alert(title: Text("Hello there!"),
        message: Text("The slider's value is \(sliderValueRounded).\n" +
                      "You earned \(pointsForCurrentRound()) points."),
        dismissButton: .default(Text("Awesome!")) {
          self.startNewRound()
        }
  )
}
'打我!'按钮的新外观
PMA'CAZ NA!'西格西的PEL Yaeg

介绍 ViewModifier

If you look at body in its current state, you’ll see a lot of repetition. For starters, there are instances where the following methods are called on a Text view:

.font(Font.custom("Arial Rounded MT Bold", size: 18))
.foregroundColor(Color.white)
.font(Font.custom("Arial Rounded MT Bold", size: 24))
.foregroundColor(Color.yellow)
.shadow(color: Color.black, radius: 5, x: 2, y: 2)
// View modifiers
// ==============

struct LabelStyle: ViewModifier {
  func body(content: Content) -> some View {
    content
      .font(Font.custom("Arial Rounded MT Bold", size: 18))
      .foregroundColor(Color.white)
      .shadow(color: Color.black, radius: 5, x: 2, y: 2)
  }
}
struct ValueStyle: ViewModifier {
  func body(content: Content) -> some View {
    content
      .font(Font.custom("Arial Rounded MT Bold", size: 24))
      .foregroundColor(Color.yellow)
      .shadow(color: Color.black, radius: 5, x: 2, y: 2)
  }
}
// Target row
HStack {
  Text("Put the bullseye as close as you can to:").modifier(LabelStyle())
  Text("\(target)").modifier(ValueStyle())
}
// Slider row
HStack {
  Text("1").modifier(LabelStyle())
  Slider(value: $sliderValue, in: 1...100)
  Text("100").modifier(LabelStyle())
}
// Score row
HStack {
  Button(action: {
    self.startNewGame()
  }) {
    Text("重来")
  }
  Spacer()
  Text("Score:").modifier(LabelStyle())
  Text("\(score)").modifier(ValueStyle())
  Spacer()
  Text("Round").modifier(LabelStyle())
  Text("\(round)").modifier(ValueStyle())
  Spacer()
  NavigationLink(destination: AboutView()) {
    Text(" 信息 ")
  }
}
.padding(.bottom, 20)
应用程序,用viewmodifiers打造了
PXA ENS,YFXVIR TUZS SOAGMUQEBAOQP

一些重构和更多的造型

You may have noticed that both LabelStyleValueStyle have one line of code in common — the line that adds a shadow:

.shadow(color: Color.black, radius: 5, x: 2, y: 2)
struct Shadow: ViewModifier {
  func body(content: Content) -> some View {
    content
      .shadow(color: Color.black, radius: 5, x: 2, y: 2)
  }
}
func body(content: Content) -> some View
struct LabelStyle: ViewModifier {
  func body(content: Content) -> some View {
    content
      .font(Font.custom("Arial Rounded MT Bold", size: 18))
      .foregroundColor(Color.white)
      .modifier(Shadow())
  }
}

struct ValueStyle: ViewModifier {
  func body(content: Content) -> some View {
    content
      .font(Font.custom("Arial Rounded MT Bold", size: 24))
      .foregroundColor(Color.yellow)
      .modifier(Shadow())
  }
}
// Button row
Button(action: {
  print("Points awarded: \(self.pointsForCurrentRound())")
  self.alertIsVisible = true
}) {
  Text("Hit me!")
    .font(Font.custom("Arial Rounded MT Bold", size: 18))
    .foregroundColor(Color.black)
}
.background(Image("Button")
  .modifier(Shadow())
)
.alert(isPresented: $alertIsVisible) {
  Alert(title: Text(alertTitle()),
        message: Text(scoringMessage()),
        dismissButton: .default(Text("Awesome!")) {
          self.startNewRound()
    })
}
// Score row
HStack {
  Button(action: {
    self.startNewGame()
  }) {
    Text("重来")
  }
  .background(Image("Button")
    .modifier(Shadow())
  )
  Spacer()
  Text("Score:").modifier(LabelStyle())
  Text("\(score)").modifier(ValueStyle())
  Spacer()
  Text("Round").modifier(LabelStyle())
  Text("\(round)").modifier(ValueStyle())
  Spacer()
  NavigationLink(destination: AboutView()) {
    Text(" 信息 ")
  }
  .background(Image("Button")
    .modifier(Shadow())
  )
}
.padding(.bottom, 20)
所有按钮现在都看起来像按钮
IKB PFO KAGSEXK QAC KUIF QAHE PEWVUNK

.padding(.bottom, 20)
.padding(.leading, 20)
.padding(.trailing, 40)
struct ButtonLargeTextStyle: ViewModifier {
  func body(content: Content) -> some View {
    content
      .font(Font.custom("Arial Rounded MT Bold", size: 18))
      .foregroundColor(Color.black)
  }
}

struct ButtonSmallTextStyle: ViewModifier {
  func body(content: Content) -> some View {
    content
      .font(Font.custom("Arial Rounded MT Bold", size: 12))
      .foregroundColor(Color.black)
  }
}
// Button row
Button(action: {
  print("Points awarded: \(self.pointsForCurrentRound())")
  self.alertIsVisible = true
}) {
  Text("Hit me!").modifier(ButtonLargeTextStyle())
}
.background(Image("Button")
  .modifier(Shadow())
)
.alert(isPresented: $alertIsVisible) {
  Alert(title: Text(alertTitle()),
        message: Text(scoringMessage()),
        dismissButton: .default(Text("Awesome!")) {
          self.startNewRound()
    })
}
// Score row
HStack {
  Button(action: {
    self.startNewGame()
  }) {
    Text("重来").modifier(ButtonSmallTextStyle())
  }
  .background(Image("Button")
    .modifier(Shadow())
  )
  Spacer()
  Text("Score:").modifier(LabelStyle())
  Text("\(score)").modifier(ValueStyle())
  Spacer()
  Text("Round").modifier(LabelStyle())
  Text("\(round)").modifier(ValueStyle())
  Spacer()
  NavigationLink(destination: AboutView()) {
    Text(" 信息 ").modifier(ButtonSmallTextStyle())
  }
  .background(Image("Button")
    .modifier(Shadow())
  )
}
.padding(.bottom, 20)
.padding(.leading, 20)
.padding(.trailing, 40)
所有按钮现在都有风格的文本
OXR XMA LOKSOKM NUP VANE SHJWOH GEQG

将图像放在按钮内

让我们加入更多的视觉风格 靶心:象的图标 重来 信息 纽扣。他们在这件事中 startovericon. 信息 icon. 在资产目录中的图像集:

 信息 icon.和StartOverIcon在资产目录
OkteEzaj AVJ ZlawcOkiwUdel UF VFU enqoq gegumuc

小按钮中的子视图
NGA Qumhiimq AV Slo Vyivt Lizturh

// Score row
HStack {
  Button(action: {
    self.startNewGame()
  }) {
    HStack {
      Image("startovericon.")
      Text("重来").modifier(ButtonSmallTextStyle())
    }
  }
  .background(Image("Button")
    .modifier(Shadow())
  )
  Spacer()
  Text("Score:").modifier(LabelStyle())
  Text("\(score)").modifier(ValueStyle())
  Spacer()
  Text("Round").modifier(LabelStyle())
  Text("\(round)").modifier(ValueStyle())
  Spacer()
  NavigationLink(destination: AboutView()) {
    HStack {
      Image(" 信息 icon.")
      Text(" 信息 ").modifier(ButtonSmallTextStyle())
    }
  }
  .background(Image("Button")
    .modifier(Shadow())
  )
}
.padding(.bottom, 20)
.padding(.leading, 20)
.padding(.trailing, 40)
使用按钮图像的应用程序
TLE CHOM DAFX SUHBEW ULIZAC

添加重音颜色

iOS的微妙适用颜色的用户界面元素以给予用户的提示的东西是活性的,可点击,可移动的或突出显示。这些所谓的 口音颜色 默认情况下,我们在改变之前在许多控件上看到了相同的蓝色 靶心 用户界面。即使你所做的一切调整,你仍然可以看到滚动条上的默认口音的颜色,而在按钮图标:

默认的口音颜色
Cya Kuruudy Atkenb Kuhik

// Slider row
HStack {
  Text("1").modifier(LabelStyle())
  Slider(value: $sliderValue, in: 1...100)
    .accentColor(Color.green)
  Text("100").modifier(LabelStyle())
}
滑块,其新的自定义强调色
Zgi vtigax,hubf owj jiv qivpiz ojbilz virof

午夜蓝
Sarraclk Kseo.

// Colors
let midnightBlue = Color(red: 0,
                         green: 0.2,
                         blue: 0.4)
// Score row
HStack {
  Button(action: {
    self.startNewGame()
  }) {
    HStack {
      Image("startovericon.")
      Text("重来").modifier(ButtonSmallTextStyle())
    }
  }
  .background(Image("Button")
    .modifier(Shadow())
  )
  Spacer()
  Text("Score:").modifier(LabelStyle())
  Text("\(score)").modifier(ValueStyle())
  Spacer()
  Text("Round").modifier(LabelStyle())
  Text("\(round)").modifier(ValueStyle())
  Spacer()
  NavigationLink(destination: AboutView()) {
    HStack {
      Image(" 信息 icon.")
      Text(" 信息 ").modifier(ButtonSmallTextStyle())
    }
  }
  .background(Image("Button")
    .modifier(Shadow())
  )
}
.padding(.bottom, 20)
.padding(.leading, 20)
.padding(.trailing, 40)
.accentColor(midnightBlue)

一些Swifui局限性

SwiftUI仍然是一个新的框架,你应该期望它有局限性。它不能(还)尽一切的UIKit可以做。

“关于”屏幕

既然你用主屏幕打造了一个主屏幕,让我们对“有类似治疗的屏幕来做同样的事情。

// View modifiers
// ==============

struct AboutHeadingStyle: ViewModifier {
  func body(content: Content) -> some View {
    content
      .font(Font.custom("Arial Rounded MT Bold", size: 30))
      .foregroundColor(Color.black)
      .padding(.top, 20)
      .padding(.bottom, 20)
  }
}

struct AboutBodyStyle: ViewModifier {
  func body(content: Content) -> some View {
    content
      .font(Font.custom("Arial Rounded MT Bold", size: 16))
      .foregroundColor(Color.black)
      .padding(.leading, 60)
      .padding(.trailing, 60)
      .padding(.bottom, 20)
  }
}
var body: some View {
  VStack {
    Text("🎯 Bullseye 🎯")
      .modifier(AboutHeadingStyle())
    Text("This is Bullseye, the game where you can win points and earn fame by dragging a slider.")
      .modifier(AboutBodyStyle())
    Text("Your goal is to place the slider as close as possible to the target value. The closer you are, the more points you score.")
      .modifier(AboutBodyStyle())
    Text("Enjoy!")
      .modifier(AboutBodyStyle())
  }
}
关于景观,带风格的文本
Uzeutwous,Yovd Gkbsep Jafy

// Constants
let beige = Color(red: 1.0,
                  green: 0.84,
                  blue: 0.70)
var body: some View {
  VStack {
    Text("🎯 Bullseye 🎯")
      .modifier(AboutHeadingStyle())
    Text("This is Bullseye, the game where you can win points and earn fame by dragging a slider.")
      .modifier(AboutBodyStyle())
      .lineLimit(nil)
    Text("Your goal is to place the slider as close as possible to the target value. The closer you are, the more points you score.")
      .modifier(AboutBodyStyle())
    Text("Enjoy!")
      .modifier(AboutBodyStyle())
  }
  .background(beige)
}
关于米色vstack
okueznuut,gagy xmu qiaho nkjaww

var body: some View {
  Group {
    VStack {
      Text("🎯 Bullseye 🎯")
        .modifier(AboutHeadingStyle())
      Text("This is Bullseye, the game where you can win points and earn fame by dragging a slider.")
        .modifier(AboutBodyStyle())
      Text("Your goal is to place the slider as close as possible to the target value. The closer you are, the more points you score.")
        .modifier(AboutBodyStyle())
      Text("Enjoy!")
        .modifier(AboutBodyStyle())
    }
    .background(beige)
  }
  .background(Image("背景"))
}
最终的观察
ttu hucov oraorogaud

一些最后的触感

让我们添加一些额外的功能带来的SwiftUI版本 靶心 有一点更接近原始的UIKit版本。

在每场比赛开始时随机化滑块的位置和每年的开始

让我们把游戏多一点通过随机滑块的位置,在每一轮的开始,包括圆在比赛开始挑战。

func startNewRound() {
  score = score + pointsForCurrentRound()
  sliderValue = Double.random(in: 1...100)
  target = Int.random(in: 1...100)
}

func startNewGame() {
  score = 0
  round = 1
  sliderValue = Double.random(in: 1...100)
  target = Int.random(in: 1...100)
}
sliderValue = Double.random(in: 1...100)
target = Int.random(in: 1...100)
func resetSliderAndTarget() {
  sliderValue = Double.random(in: 1...100)
  target = Int.random(in: 1...100)
}
func startNewRound() {
  score = score + pointsForCurrentRound()
  resetSliderAndTarget()
}

func startNewGame() {
  score = 0
  round = 1
  resetSliderAndTarget()
}
      .padding(.bottom, 20)
      .padding(.leading, 20)
      .padding(.trailing, 40)
      .accentColor(midnightBlue)
    }
    .background(Image("背景"))
    .onAppear() {
      self.startNewGame()
    }
  }
  .navigationViewStyle(StackNavigationViewStyle())
}
该应用程序,在启动时
BMA IVK,HHEC Koogdyed

切换到“关于”屏幕
Jroljgeng Xe Sho“ehaan”rtmuuf

返回主屏幕
Neganfong Hajl Ri GCA VIUB XXQEIL

@State var sliderValue = 50.0
@State var sliderValue = Double.random(in: 1...100)
      .padding(.bottom, 20)
      .padding(.leading, 20)
      .padding(.trailing, 40)
      .accentColor(midnightBlue)
    }
    .background(Image("背景"))
  }
  .navigationViewStyle(StackNavigationViewStyle())
}

添加标题到主屏幕的导航栏

在主屏幕上,导航栏看起来像一个白色的半透明条带。用户甚至可能认为这是一个错误。让我们在导航栏中显示其标题来云杉。

var body: some View {
  NavigationView {
    VStack {
      Spacer().navigationBarTitle("🎯 Bullseye 🎯")
该应用程序,其导航栏标题
SJE OHJ,zayw OXM yohqe交流MQO juzecameub POZ

改进警报消息

Let’s update the alert so that it shows a title that varies with the user’s accuracy. We’ll also add a method to generate the alert’s message to simplify the Alert initializer and make its code more readable.

func alertTitle() -> String {
  let title: String
  if sliderTargetDifference == 0 {
    title= "Perfect!"
  } else if sliderTargetDifference < 5 {
    title= "You almost had it!"
  } else if sliderTargetDifference <= 10 {
    title= "Not bad."
  } else {
    title= "Are you even trying?"
  }
  return title
}

func scoringMessage() -> String {
  return "The slider's value is \(sliderValueRounded).\n" +
         "The target value is \(target).\n" +
         "You scored \(pointsForCurrentRound()) points this round."
}
Alert(title: Text(alertTitle()),
      message: Text(scoringMessage()),
      dismissButton: .default(Text("Awesome!")) {
        self.startNewRound()
      }
)
更新的警报
yri ibpetaw iyeqj.

有技术问题吗?要报告bug? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里 .

有关于网上阅读体验反馈给份额? 如果您对UI,UX,高亮反馈,或者我们的在线读者的其他功能,您可以付款给设计团队与下面的表格:

© 2021 Razeware LLC

您正在阅读的自由,以显示为本章的部分 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。