iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
Mobile Development

Swift iOS UIKit 初學者系列:從零開始開發互動式應用系列 第 27

【Day 27】iOS 開發實戰 - 棋盤 App:用 UIBezierPath 打造完整互動棋盤UI

  • 分享至 

  • xImage
  •  

導言

在這篇文章中,我們將挑戰使用 UIBezierPath 來設計並實現一個完整的互動棋盤 UI。UIBezierPath 是一個非常強大的工具,能讓我們以簡單的方式繪製複雜的形狀和路徑。在這次的練習中,我們會利用它繪製棋盤,並讓玩家可以互動放置棋子。這次整個 UI 完全使用程式碼動態生成,讓我們來看看如何只依賴程式碼來打造實現這個 UI 吧!

學習目標

透過這篇文章,你將學習到以下幾個核心技能:

  • 使用 UIBezierPath 繪製自定義圖形:理解如何利用 UIBezierPath 繪製直線、曲線以及複雜形狀,並應用於 iOS App 的 UI 設計中。
  • 結合 CAShapeLayer 渲染圖形:將 UIBezierPath 繪製的圖形通過 CAShapeLayer 進行視覺化處理,並自定義樣式。
  • 透過動畫增強視覺效果:使用 CABasicAnimation 讓棋盤的線條依序出現,增強應用的動態效果。
  • 實現互動操作:學會如何在自定義的 UIView 中處理點擊事件,並讓玩家能夠在棋盤上放置棋子。
  • 動態生成和管理 UI:完全透過程式碼動態生成 UI,不依賴 Storyboard,提升靈活性與可擴展性。

操作影片

Yes

螢幕截圖

UI 設計

  • 背景:使用隨機大小、顏色和角度的三角形點綴,讓背景更有層次感和生動性。
  • 棋盤區:設計了一個 12x12 的網格狀棋盤,每個交叉處可以放置棋子。
  • 重新開局按鈕:讓玩家能夠隨時清空棋盤,重新開始遊戲。
  • 棋子形狀選擇:提供多種棋子形狀,包括圓形、心形、方形、星形、三角形等,增添遊戲趣味。

基礎知識

在這篇文章中,我們將使用到以下幾個 iOS 開發的核心技術:

  • CALayer(): CALayer 是 Core Animation 框架中的基本類之一,用於管理可視化內容。它是一個抽象類,可以被子類化來實現不同的視覺效果和行為。CALayer 可以用於製作動畫、裝載圖像、顯示文本等。
  • UIBezierPath:用來繪製棋盤的線條和各種棋子的形狀。
  • CAShapeLayer:透過路徑來渲染圖形,實現棋盤和棋子的可視化。
  • CABasicAnimation:用來讓棋盤的繪製有動畫效果,讓線條一條條地顯示出來,增加視覺上的動態感。

這些技術相互結合,使得我們能夠創建一個富有交互性和視覺效果的遊戲介面。

程式筆記

1. 預覽畫面:

使用 Xcode 15 提供的 #Preview 功能,讓我們能即時看到 UIBezierPath 繪製的效果。這有助於在開發過程中快速調整圖形和界面。

#Preview {
    UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()!
}

2. 繪製背景三角形:

UIBezierPath 是 iOS 中用來繪製 2D 向量路徑的工具。它可以用來畫直線、曲線、矩形、圓形、橢圓形等,甚至是任意多邊形或複雜形狀。

在這個例子中,我們使用 UIBezierPath 和 CAShapeLayer 來畫出一個三角形,並且可以利用 fillColor 屬性來更改三角形的顏色。。UIBezierPath 有以下幾個關鍵步驟:

  • 建立路徑:首先創建一個空的 UIBezierPath 物件,這個物件會記錄你後續所畫的所有線段和曲線。
  • 定義路徑的起點:使用 move(to:) 方法來設置畫圖的起點。這個點是畫線段的起始位置。
  • 添加線段:使用 addLine(to:) 方法從上一個點連接到新的座標點,逐步繪製出所需的形狀。這裡我們定義了三條線,形成一個三角形。
  • 封閉路徑:使用 close() 方法封閉圖形,會自動從最後一個點連接回起點,形成封閉的圖形。
  • 視覺化圖形:在定義完路徑後,我們需要將這個路徑「渲染」出來。這時我們可以使用 CAShapeLayer 來承載這條路徑,並用 fillColor 設定填充顏色。視覺化圖形:在定義完路徑後,我們需要將這個路徑「渲染」出來。這時我們可以使用 CAShapeLayer 來承載這條路徑,並用 fillColor 設定填充顏色。
  • 將形狀加入視圖:最後一步是將這個形狀加入到視圖的 layer 中,這樣它才會真正顯示在界面上。
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 0, y: 0))
        path.addLine(to: CGPoint(x: 110, y: 0))
        path.addLine(to: CGPoint(x: 90, y: 70))
        path.close()
        
        let triangleLayer = CAShapeLayer()
        triangleLayer.path = path.cgPath
        triangleLayer.fillColor = UIColor.init(red: 0.7, green: 0.5, blue: 0.5, alpha: 0.5).cgColor
        
        view.layer.addSublayer(triangleLayer)

3. 繪製棋盤:

棋盤由 12x12 的網格組成,使用 UIBezierPath 繪製直線,通過 move(to:) 和 addLine(to:) 定義起點和終點來完成線條的繪製,並且我們加入了 CABasicAnimation 讓線條一條條地顯示,增添動感。

        let boardWidth = 300.0
        let boardHeight = 300.0
        let numRows = 12
        let numCols = 12
        
        let cellWidth = boardWidth / CGFloat(numCols)
        let cellHeight = boardHeight / CGFloat(numRows)
        
        let path = UIBezierPath()

        for i in 0...numRows {
            let y = CGFloat(i) * cellHeight
            path.move(to: CGPoint(x: 50, y: y+100))
            path.addLine(to: CGPoint(x: boardWidth+50, y: y+100))
        }

        for i in 0...numCols {
            let x = CGFloat(i) * cellWidth
            path.move(to: CGPoint(x: x+50, y: 0+100))
            path.addLine(to: CGPoint(x: x+50, y: boardHeight+100))
        }
        
        UIColor.lightGray.setStroke()
        path.lineWidth = 1.0
        
        let lineLayer = CAShapeLayer()
        lineLayer.path = path.cgPath
        lineLayer.strokeColor = UIColor.lightGray.cgColor
        lineLayer.lineWidth = 1.0
        
        view.layer.addSublayer(lineLayer)

4. 棋子的交互操作:

接下來,我們要處理棋子的放置。由於 CAShapeLayer 不支援事件處理,所以我們需要將棋盤搬到 UIView 裡來處理互動。為了實現棋子放置的功能,我們覆寫了 touchesBegan 方法,這個方法能捕捉到使用者的觸控事件。

當玩家點擊棋盤上的某個位置時,我們會計算點擊的座標,並透過算法找到與此座標最接近的棋盤交叉點,這樣就能精準地在棋盤上放置棋子。棋子的顏色會根據玩家的回合輪流變換,模擬出雙方對弈的效果。以下是實現邏輯的示範程式碼:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let touchLocation = touch.location(in: self)
        
        // 找到與點擊座標最接近的棋盤交叉點
        let nearestPoint = findNearestIntersection(for: touchLocation)
    
        // 在最近的交叉點放置棋子
        placePiece(at: nearestPoint)
    }

透過這個步驟,玩家的每一次點擊都會觸發棋子的放置動作,使得棋盤上的互動變得自然且準確。

5. 提示最新棋子位置:

利用 CAShapeLayer 和 UIBezierPath 繪製一個特別的外框,提示玩家最近一次放置的棋子位置,增強遊戲的互動性。

6. 多樣棋子形狀:

我們使用 UIBezierPath 繪製了多種棋子形狀,包括圓形、心形、方形和星形,並且將它們動態添加到棋盤中,讓玩家可以自由選擇棋子的形狀。每個棋子形狀都是一個 UIView,其 layer 使用 UIBezierPath 來繪製圖形,最後這些棋子形狀會加入棋盤視圖中。

結語

在這篇文章中,我們探索了如何使用 UIBezierPath、CAShapeLayer 和 CABasicAnimation,實現一個具有豐富交互性和視覺效果的棋盤 App。本次設計完全依靠程式碼動態生成,使界面設計更具靈活性和可擴展性。希望你也能從中學到更多有關 iOS 繪圖和動畫的技巧,並將這些知識應用到你的開發中。


上一篇
【Day 26】iOS 開發實戰 - 廣播電台 App:程式碼生成 UI 與 AVPlayer 串流播放
系列文
Swift iOS UIKit 初學者系列:從零開始開發互動式應用27
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言