UIKit

如何在視圖和按鈕上設定特定的圓角 (Rounded Corners)

我們目前正進行一個新系列的教學,主要回答一些常見問題,從簡單的初學者問題、到複雜的進階問題亦會覆蓋到。今天我們的問題是:我知道如何在視圖 (View) 或按鈕 (Button) 上設定圓角 (Rounded Corners)。但如果我只想設定某一角為圓角,並非所有角都設定為圓角,要如何在 Swift 實現呢?
如何在視圖和按鈕上設定特定的圓角 (Rounded Corners)
如何在視圖和按鈕上設定特定的圓角 (Rounded Corners)
In: UIKit

我們目前正進行一個新系列的教學,主要回答一些常見問題,從簡單的初學者問題、到複雜的進階問題亦會覆蓋到。今天我們的問題是:

我知道如何在視圖 (View) 或按鈕 (Button) 上設定圓角 (Rounded Corners)。但如果我只想設定某一角為圓角,並非所有角都設定為圓角,要如何在 Swift 實現呢?

好的,先回看一下如何在視圖設定圓角,Apple 簡化了建立圓角視圖的步驟,你所需要做的就只是設定視圖圖層 (Layer) 中 cornerRadius 的屬性,並將 clipsToBounds 設為 true。請參考以下的程式碼:

self.view.layer.cornerRadius = 20.0
self.view.clipToBounds = true

為了看到實作的結果,你可以建立一個 Playgrounds 專案,並輸入下列程式碼:

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
    var cardView: UIView!

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .black

        cardView = UIView()
        view.addSubview(cardView)

        cardView.translatesAutoresizingMaskIntoConstraints = false
        cardView.widthAnchor.constraint(equalToConstant: 200).isActive = true
        cardView.heightAnchor.constraint(equalToConstant: 200).isActive = true
        cardView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        cardView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

        cardView.backgroundColor = UIColor(red: 1.0, green: 0.784, blue: 0.2, alpha: 1.0)

        self.view = view
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        cardView.roundCorners(cornerRadius: 20.0)
    }

}

extension UIView {

    func roundCorners(cornerRadius: Double) {

        self.layer.cornerRadius = CGFloat(cornerRadius)

        self.clipsToBounds = true

    }
}


// Present the view controller in the Live View window

PlaygroundPage.current.liveView = MyViewController()

切換到 Asssistant Editor 模式,你應該可以看到視圖如下圖一樣,是一個有圓角的黃色視圖。

rounded corners

很簡單吧!但這個視圖所有角都是圓角,如果你並不想把所有角都設定成圓角呢?譬如說,你只想把上方兩個角設定成圓角,要怎樣做呢?

在 iOS 11 使用 maskedCorners

在 iOS 11,Apple 為 Core Animation Layer (CALayer) 推出了一個新的屬性 maskedCorners,這個屬性屬於 CACornerMask 型別,並包含 4 個屬性值:

  • layerMaxXMaxYCorner – 右下角
  • layerMaxXMinYCorner – 右上角
  • layerMinXMaxYCorner – 左下角
  • layerMinXMinYCorner – 左上角

maskedCorners 的默認屬性為顯示全部四個角,現在如果你只想設定上面兩個角為圓角,你可以這麼設定 maskedCorners

self.view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

如果你依照下文修改了 roundCorners 方法,就可以只把黃色視圖上方兩個角設定成圓角。

func roundCorners(cornerRadius: Double) {

    self.layer.cornerRadius = CGFloat(cornerRadius)

    self.clipsToBounds = true

    self.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

}

在 iOS 10 或以下的版本使用 Bezier 路徑

剛討論的方法目前僅能支援 iOS 11 以上的版本,如果你的 App 需要支援之前的 iOS 版本,就不能使用 maskedCorners 屬性。

這裡來介紹另一種方式來替代 maskedCorners。我們可以使用 UIBezierPath 來建立圓角矩形路徑,這樣初始化 UIBezierPath,我們就可以設定某個特定的角為圓角:

let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 10.0, height: 10.0))

依著路徑,我們可以來建立一個形狀圖層作為 Mask,請依下文更新 roundCorners 的方法:

func roundCorners(cornerRadius: Double) {
    let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))

    let maskLayer = CAShapeLayer()
    maskLayer.frame = self.bounds
    maskLayer.path = path.cgPath
    self.layer.mask = maskLayer

}

我們建立了一個上方兩個角為圓角的 Mask,然後設定了視圖圖層的 mask 屬性來覆蓋內容。這樣我們就可以在 iOS 10 或以下的版本,來設定視圖與按鈕特定一個角為圓角了。

請留意當視圖出現時,可能出現的 Rendering(渲染)現象。你需要在 viewDidAppear()viewDidLayoutSubviews() 內呼叫 roundCorners 方法:

override func viewDidLayoutSubviews() {
    cardView.roundCorners(cornerRadius: 20.0)
}

成果應如下圖所顯示:

rounded-corners-uiview-2

利用動畫展示角的變化

有些讀者可能會想知道如何利用動畫展示角的變化,你可以使用 UIView 動畫或是新的 UIViewPropertyAnimator 來製作。

譬如說,你想製作一個動畫,當使用者點擊方形視圖時,角就會有變化。首先,你需要在 viewDidLoad() 登記一個點擊辨識:

let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(animateCornerChange(recognizer:)))
cardView.addGestureRecognizer(tapRecognizer)

接著,如此建立一個 animateCornerChange 方法:

@objc func animateCornerChange(recognizer: UITapGestureRecognizer) {
    let targetRadius: CGFloat = (cardView.layer.cornerRadius == 0.0) ? 100.0 : 0.0
    UIViewPropertyAnimator(duration: 1.0, curve: .easeInOut) {
        self.cardView.layer.cornerRadius = targetRadius
        }.startAnimation()
}

在以上的程式碼,我們使用 UIViewPropertyAnimator 來建立動畫;你亦可以使用標準 UIView 動畫來展示角的變化:

 UIView.animate(withDuration: 1.0, delay: 0.0, options: .curveEaseInOut, animations: {
     self.cardView.layer.cornerRadius = targetRadius
 }, completion: nil)
 

如果你已經在 Playground 專案更新程式碼,點擊視圖後會啟動動畫。

rounded-corners-uiview-3

本次的教學就到這裡。請密切留意我們的專頁,我們會陸續更新文章。歡迎你就這個新系列留言提出想法。

譯者簡介:Oliver Chen-工程師,喜歡美麗的事物,所以也愛上 Apple,目前在 iOS 程式設計上仍是新手,正研讀 Swift 與 Sketch 中。生活另一個身份是兩個孩子的爸,喜歡和孩子一起玩樂高,幻想著某天自己開發的 App,可以讓孩子覺得老爸好棒!。聯絡方式:電郵[email protected]

原文How to Create Top/Bottom Rounded Corners for Views and Buttons

作者
Simon Ng
軟體工程師,AppCoda 創辦人。著有《iOS 16 App 程式設計實戰心法》、《iOS 16 App程式設計進階攻略》以及《精通SwiftUI》。曾任職於HSBC, FedEx等跨國企業,專責軟體開發、系統設計。2012年創立AppCoda技術部落格,定期發表iOS程式教學文章。現時專注發展AppCoda業務,致力於iOS程式教學、產品設計及開發。你可以到推特與我聯絡。
評論
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。