(Swift) How to adjust the size of TabBarItem image size and customize TabBarController

Photo by Harpal Singh on Unsplash

目的:學習如何正確製作出符合 TabBarItem 中 image 的尺寸,並客製化 TabBarController

Icons 素材網站

使用上述的網站下載 icon,最常用到的問題,就是使用在 TabBarController 下方的 image 大小到底該設定為多少?我找到了兩種方法。

第一種方法

首先先到任意的 icon 網站下載你所需的 icon

source

下載後的 icon ,若是直接拖入 Xcode 中的 Assets,會出現 icon 過大的問題

這時後,我們必須調整原本的圖片像素,調整為 Apple 所預設的 tab bar icons 的像素大小,並產生 3 種不同的大小尺寸丟入 Assets 中

source

點選原本的圖片(Preview開啟),點選上方 Tools,再點選 Adjust Size…

調整 Width 與 Height 至 75 ,並選取 pixels ,最後點選 OK

再來重新將調整後的照片拖入 Assets 中,並將照片拖到 3X

點選原本的圖片,將照片調整 Width 與 Height 至 50,產生出第二張圖片

將調整後的照片拖入 Assets 中的 2X

點選原本的圖片,將照片調整 Width 與 Height 至 25,產生出第三張圖片,並將調整後的照片拖入 Assets 中的 1X

再次執行程式

目前的 icon 似乎大小已經正常了,不過想要顯示原本 icon 的顏色需要設定一下

打開 Assets 選取圖片,點選 Render As 後,選取 Original image

執行程式

第二種方法

使用他人開源的 GitHub 程式碼,來一次產生 3 個尺度的 Tab Bar Icon

下載專案

打開專案後,點選 Presenter.swift,修改 function createImageSetFrom 中的尺寸,75 -> 37.550 -> 2525 -> 12.5

func createImageSetFrom(image: NSImage, with name: String, at path: URL) {
let folderPath = path.appendingPathComponent("\(name).imageset")
var normalizedFolderPath = folderPath.absoluteString
lastImagesetURL = folderPath

let prefix = "file://"

normalizedFolderPath = normalizedFolderPath.replacingOccurrences(of: prefix, with: "")
normalizedFolderPath = normalizedFolderPath.removingPercentEncoding ?? "\(normalizedFolderPath)"

createFolderAtPath(at: normalizedFolderPath)

let image3x = resizeImage(image: image, width: 37.5, height: 37.5)
let image2x = resizeImage(image: image, width: 25, height: 25)
let image1x = resizeImage(image: image, width: 12.5, height: 12.5)

saveImage(image: image3x!, path: folderPath.appendingPathComponent("\(name)@3x.png"))
saveImage(image: image2x!, path: folderPath.appendingPathComponent("\(name)@2x.png"))
saveImage(image: image1x!, path: folderPath.appendingPathComponent("\(name)@1x.png"))

saveJSON(path: folderPath, imageName: name)
}

執行程式後,將點選選取照片或是將照片拖入其中

點選 Export 將轉換後的照片保存起來

將下載後的資料夾直接拖入 Xcode 中的 Assets

執行結果

使用 Github 開源的程式,你就能更快速產生出 符合 Tab Bar Icon 大小的 Image

由於我們現在讓 Image 顯示原本的顏色,這時會導致有點不清楚我們切換到哪一個頁面,接下來就會使用 客製化的 TabBarController 來解決這個問題

客製 TabBarController

點選 TabBarController 下方的 Tab Bar,再將原本 Translucent 取消,並將 Background 設為 Default

接下來新增一個 Cocoa Touch Class TabbarController

接下來將主頁的 TabbarController 的 Class 設為 TabbarController

接下來輸入以下程式碼到 TabbarController

import UIKit

class TabbarController: UITabBarController {

var upperLineView: UIView!

let spacing: CGFloat = 12

override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2){
self.addTabbarIndicatorView(index: 0, isFirstTime: true)
}

}

// 添加 TabBar 項目指示器的上方線條
func addTabbarIndicatorView(index: Int, isFirstTime: Bool = false){
// 獲取指定索引處 TabBar 項目的視圖
guard let tabView = tabBar.items?[index].value(forKey: "view") as? UIView else {
return
}
if !isFirstTime{
upperLineView.removeFromSuperview()
}
// 創建上方線條視圖
upperLineView = UIView(frame: CGRect(x: tabView.frame.minX + spacing, y: tabView.frame.minY + 0.1, width: tabView.frame.size.width - spacing * 2, height: 4))
upperLineView.backgroundColor = UIColor.systemBlue // 設定線條視圖的顏色
tabBar.addSubview(upperLineView)
}

override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
tabBar.tintColor = nil // 取消 TabBar 項目的特定選中顏色
item.image?.withRenderingMode(.alwaysTemplate) // 設定 TabBar 項目圖標渲染模式
}

}

extension TabbarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
addTabbarIndicatorView(index: self.selectedIndex) // 選中 TabBar 項目時,添加指示器視圖
}
}

✨若是想使用單色的 icon 的話, tabBar.tintColor = nil 必須註解掉

再次新增一個 Cocoa Touch Class TabBarWithCorners ,並將主頁的 Tabbar 的 Class 設為 TabBarWithCorners

接下來輸入以下程式碼到 TabBarWithCorners

import UIKit

@IBDesignable // 用於標記自定義視圖類別,以便在 Interface Builder 中進行即時的設計預覽
class TabBarWithCorners: UITabBar {

@IBInspectable var color: UIColor? // 可在 Interface Builder 中設定的屬性,用於設定 Tab Bar 的背景顏色
@IBInspectable var radii: CGFloat = 18 // 可在 Interface Builder 中設定的屬性,用於設定 Tab Bar 圓角半徑

private var shapeLayer: CALayer?

override func draw(_ rect: CGRect) {
addShape() // 在 Tab Bar 上添加自定義的形狀圖層
}

private func addShape() {
let shapeLayer = CAShapeLayer()

shapeLayer.path = createPath() // 創建 Tab Bar 的形狀路徑
shapeLayer.strokeColor = UIColor.gray.cgColor
shapeLayer.fillColor = color?.cgColor ?? UIColor.white.cgColor // 設定 Tab Bar 的填充顏色,如果 color 屬性為空則使用白色
shapeLayer.lineWidth = 0
shapeLayer.shadowColor = UIColor.gray.cgColor
shapeLayer.shadowOffset = CGSize(width: 0, height: -2);
shapeLayer.shadowOpacity = 0.21
shapeLayer.shadowRadius = 8
shapeLayer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radii).cgPath // 設定 Tab Bar 的陰影效果

if let oldShapeLayer = self.shapeLayer {
layer.replaceSublayer(oldShapeLayer, with: shapeLayer) // 替換舊的形狀圖層
} else {
layer.insertSublayer(shapeLayer, at: 0) // 添加形狀圖層到 Tab Bar 的最底層
}

self.shapeLayer = shapeLayer
}

private func createPath() -> CGPath {
let path = UIBezierPath(
roundedRect: bounds, // Tab Bar 的範圍
byRoundingCorners: [.topLeft, .topRight], // 設定 Tab Bar 左上和右上兩個角為圓角
cornerRadii: CGSize(width: radii, height: 0.0)) // 設定圓角的半徑

return path.cgPath
}

override func layoutSubviews() {
super.layoutSubviews()
self.isTranslucent = true
var tabFrame = self.frame

var bottomSafeAreaHeight: CGFloat = 0

if #available(iOS 15.0, *) {
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
bottomSafeAreaHeight = windowScene.windows
.filter({$0.isKeyWindow})
.first?
.safeAreaInsets.bottom ?? 0
}
} else {
bottomSafeAreaHeight = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0
}

tabFrame.size.height = 65 + bottomSafeAreaHeight
tabFrame.origin.y = self.frame.origin.y + self.frame.height - 65 - bottomSafeAreaHeight

self.layer.cornerRadius = 18 // 設定 Tab Bar 的圓角半徑
self.frame = tabFrame
self.items?.forEach({ $0.titlePositionAdjustment = UIOffset(horizontal: 0.0, vertical: -5.0) }) // 調整 Tab Bar 項目的標題位置
}
}

使用 @IBDesignable 我們就能無需執行程式,也能看到 TabBar 的設計

執行程式

GitHub

Reference

--

--