江子安
海大 SwiftUI iOS / Flutter App 程式設計
11 min readOct 23, 2022

--

#2 酷炫動畫電子書 — Disney world

這次要做的是有酷炫動畫的電子書,因為很喜歡迪士尼的動畫和氣氛,所以選擇disney來當作這次的主題

一、成果MP4

demo video

二、Github link

三、iPhone、iPad截圖

left:ipad pro(11 inchs); right:iphone 14 pro

四、icon設置(Xcode14以上適用)

icon

Step1:找一張圖片把他size改成1024*1024pixel

Step2:轉成png檔

Step3:加到appicon裡面

五、動畫

開頭Mickey和Minnie慢慢靠近最後親再一起的動畫

kiss animation

我是用moveDistance這個變數去控制兩個人的位置

當看到米奇和米妮後(onApper),moveDistance的值就會增加

比較要注意的是因為兩個人的移動方向相反,所以Minnie’s offset=movDistance, Nickey’s offset=-movDistance

easeout(前面快後面慢)用來強調他們最後快親到的畫面

HStack {
Image("mickny")
.resizable()
.scaledToFit()
.clipShape(Circle())
.offset(x: moveDistance, y: 0)
.animation(.easeOut(duration: 4), value: moveDistance)
.onAppear(){
moveDistance+=12
}
Image("micky")
.resizable()
.scaledToFit()
.clipShape(Circle())
.offset(x: -moveDistance, y: 0)
.animation(.easeOut(duration: 4), value: moveDistance)
.onAppear(){
moveDistance+=20
}
}

六、不同字型

因為這次的主題是迪士尼,想要營造可愛活潑的感覺,但預設的字型太呆板,所以額外設定很多不同的字型讓畫面更活潑

Step1:上網下載otf , tff 檔的字型(網路上有很多免費的字型可以下載)

suggest: https://www.1001fonts.com/?page=21

Step2:將下載的檔案加到Xcode的file內

Xcode file

Step3:在info的font provide by application 加入otf , tff檔

Step4:只要在目標Text()後面加上.font(.custom(“//字行名稱”))就可以拉!

Text("Click me to know more")
.font(.custom("Imagine Serif Stamp Rough", size: 30))

七、Youtube影片播放

我是用embedded的方式把youtbue嵌在page內,設定好之後只要改後面的id參數就可以了

struct youtubedata:UIViewRepresentable {
let videoID:String
let txt:String
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}

func updateUIView(_ uiView: WKWebView, context: Context) {
guard let youtubeURL=URL(string:"https://www.youtube.com/embed/\(videoID)")else{return}
uiView.scrollView.isScrollEnabled=false
uiView.load(URLRequest(url: youtubeURL))
}
}

八、背景音樂播放

利用AVQueuePlayer(),只要把mp3檔加到file內,再用Bundle去找到檔案路徑,加上player.play,looper就可以重複播放了

Text("This is Disney Intro!")
.font(.custom("National Cartoon", size: 30))
.foregroundColor(.orange)
.onAppear {
let player = AVQueuePlayer()
let fileUrl = Bundle.main.url(forResource: "DisneyIntro", withExtension: "mp3")!
let item = AVPlayerItem(url: fileUrl)
self.looper = AVPlayerLooper(player: player, templateItem: item)
player.play()
}

九、Tabview

tabview

利用tabview去實現切換不同頁面

TabView{
home()
.tabItem {
Label("Home", systemImage: "house")
}
movie_list()
.tabItem {
Label("Movie", systemImage:"popcorn")
}
more()
.tabItem {
Label("More",systemImage: "plus.square.on.square")
}
}

十、仿 ig page

ig post

在介紹每個動畫角色的時候,為了讓他能更符合生活,我幫每個角色設計了專屬的ig page,比起文字敘述,從簡介和照片可以更了解每個角色的個性和特色

ig 分格照片:

Step1:利用LazyVGrid達到分格的效果

Step2:加上Retangle在底下控制照片大小

Step3:把照片overlay在Right etangle上

let columns=Array(repeating: GridItem(spacing:2), count: 3)
ScrollView{
LazyVGrid(columns: columns,spacing: 2){
ForEach(0..<igd.pics.count){index in
Rectangle()
.aspectRatio(1, contentMode: .fit)
.overlay {
Image(igd.pics[index])
.resizable()
.scaledToFill()
}
.clipped()
}
}
}

十一、漸層背景

Gradient

利用LinearGradient去實作,從左上漸層到右下

LinearGradient(gradient: Gradient(colors: [Color.blue, Color.white]), startPoint: UnitPoint(x: 0, y: 0), endPoint: UnitPoint(x: 1, y: 1))
.ignoresSafeArea()

十二、實作架構說明

因為這次的主題是電子書,有很多程式碼會重複,所以我用參數傳遞的方式去寫每個page,包含三個part

同一page:

  1. pagedata:每個row要傳入的參數
  2. pagerow:list的其中一個row
  3. pagelist:整個page的畫面設定

不同page:

  1. pagedata:每個page要傳入的參數
  2. eachpageview:每個page的模板
  3. pageview:目標pagedata+eachpageview

Benefit: 1.當有很多page要用到相同的寫法時,只需要改變pagedata即可

2.要修改程式碼時,只需要改變pagerow或pageview即可

十四、部分程式碼說明

  1. 滑動
scroll

利用scrollview去實作滑動

scrollview(.horizontal):水平滑動

scrollview(.vertical):垂直滑動

2.點選圖片或文字後連接到網頁

利用Link實作

Link(destination: URL(string:linkD.url)!, label:{
VStack {
Image(linkD.pic)
.resizable()
.scaledToFit()
.clipShape(Circle())
.frame(width: 150)
Text(linkD.txt)
.fontWeight(.bold)
}

})

3.滑動的page

scroll page

利用Tabview去實作

.tabViewStyle(.page)產生可以滑動的page

.indexViewStyle(.page(backgroundDisplayMode: .always))在原點後面加上背景(目的:在light mode看得到點點)

4.List和背景

list

如果直接在list後加.backgorund(Image())會跟原本沒加一樣,那是因為list本身有預設一個白色的背景,如果想改成自己想要的背景,需要在最後加.scrollContentBackground(.hidden)把預設背景隱藏起來。

5.section分類別

section

利用section去幫每個list分組,畫面上看起來會更簡潔

6.陰影

shadow

原本的照片是長方形的,放在list上看起來很單調,為了增加海報的立體感,我在照片下方加了一個Retangle()和陰影

Image(movie.pic)
.resizable()
.scaledToFit()
.frame(width: 100)
.clipShape(Rectangle())
.overlay(Rectangle().stroke(Color.white,lineWidth: 4))
//.cornerRadius(20)
.shadow(radius: 10)

十五、心得

這次的專案花了很多時間去搞懂參數之間的傳遞,同一page跟不同page的傳遞方式不一樣,還有data跟view之間的關係。雖然花了很多時間去實作不同功能,但也真的學到很多,像是怎麼在page裡面播放影片和音樂,好幾層的navigatelink,還有字型,icon等設定。現在做出來的作品也越來越有內容,越能表達自己的想法。之後的目標是設計出能和使用者互動的App。

十六、其他

這次想做的功能很多,但礙於時間的關係有很多想法都還沒做出來

  1. ig tag可以連到別人的ig page
  2. 每個動畫的經典音樂
  3. 不用embedded就可以直接播放影片
  4. 更多酷炫的動畫
  5. gif搭配漂亮的編筐

(不知道為什麼在Simulator Screen Record那邊一直無法按右鍵)

for 封面

--

--