#33 利用 notification center 在 controller 間傳資料

在設定視圖中調整其他視圖內Label的字型大小

本次作業會使用到以下技術

  1. 用來傳遞異動訊息的notification center
  2. 用來存取字型大小數值的UserDefault
  3. 以Singleton Design Pattern來統一處理字型大小變更的Extension型別
  4. 用IBOutletCollection統一變更視圖內的Label大小

佈局

先設置字型大小與Slider拉桿異動後的位置數值存取,分別做成set與get方法用來存取UserDefault內的值,因為UIFont只吃CGFloat,所以回傳值要做轉型。


class UserDefaultsManager {
// Singleton
static let shared = UserDefaultsManager()
private init() {}

// UserDefault的Key,避免使用Hardcode
private let fontSizekey = "fontSizekey"
private let sliderValuekey = "sliderValuekey"

// 存取各視圖內Label所需的字型大小
func getFontSize() -> CGFloat {
let savedFontSize = UserDefaults.standard.float(forKey: fontSizekey)
return CGFloat(savedFontSize > 0 ? savedFontSize : 17)
}

func setFontSize(_ size: Float) {
UserDefaults.standard.set(size, forKey: fontSizekey)
}
// 存取設定視圖內Slider拉桿的位置
func getSliderValue() -> Float? {
let savedSliderValue = UserDefaults.standard.float(forKey: sliderValuekey)
return savedSliderValue
}

func setSliderValue(_ value: Float) {
UserDefaults.standard.set(value, forKey: sliderValuekey)
}

}

接著在設定ViewController內新增notification center的延伸型別,主要是避免發送訊息時打錯字。


// 程式的通知中心
extension Notification.Name {
static let doRefreshNotification = Notification.Name("doRefreshNotification")
}

在Slider的IBAction中寫一段程式碼,用來存放拉桿的值,然後透過notification center發送廣播訊息到其他視圖。

 
@IBAction func adjustFontSize(_ sender: UISlider) {

sender.value.round() // 捨棄小數點值

UserDefaultsManager.shared.setFontSize(sender.value + 17)
UserDefaultsManager.shared.setSliderValue(sender.value)

let fontSize = UserDefaultsManager.shared.getFontSize()

self.customLabelFont(size: fontSize)

NotificationCenter.default.post(name: .doRefreshNotification, object: nil) // 程式的通知中心

}

設定視圖內統一用一個function來處理不同的Lable大小,然後用forloop處理IBOutletCollection的FontSize。

    func customLabelFont(size fontSize: CGFloat) {

for label in cloudLabels {
label.font = UIFont.systemFont(ofSize: fontSize)
}
sampleFontLabel.font = UIFont.systemFont(ofSize: fontSize)
codeLabel.font = UIFont.systemFont(ofSize: fontSize)
}

IBOutletCollection連接如下圖,注意是Array型別。

然後要在設定控制器的ViewDidLoad配置好每次進入時更新Label大小與Slider拉桿值,其他控制器只需要更新Label大小,用UserDefaultsManager讀取資料然後改變元件Font Size。

    
override func viewDidLoad() {
super.viewDidLoad()

updateUI()
}

func updateUI() {

if let sliderValue = UserDefaultsManager.shared.getSliderValue() {

fontSize.value = sliderValue // 設定拉桿值

let fontSize = UserDefaultsManager.shared.getFontSize()
self.customLabelFont(size: fontSize)
}
}


func customLabelFont(size fontSize: CGFloat) {

for label in cloudLabels {
label.font = UIFont.systemFont(ofSize: fontSize)
}
sampleFontLabel.font = UIFont.systemFont(ofSize: fontSize)
codeLabel.font = UIFont.systemFont(ofSize: fontSize)
}

其他視圖控制器則要搭配notification center接收異動通知並更新元件Font Size,下面是TableView的設定範例。


class ListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

var fontSize: CGFloat = 17 // 用來接收UserDefault的回傳值

override func viewDidLoad() {
super.viewDidLoad()

// Do any additional setup after loading the view.

updateUI()

// 觸發通知中心時自動刷新新的訊息
NotificationCenter.default.addObserver(self,
selector: #selector(updateUI),
name: .doRefreshNotification,
object: nil)
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateUI()
}

@objc
func updateUI() {
self.fontSize = UserDefaultsManager.shared.getFontSize()
self.tableView.setCustomCellLabelFont(size: fontSize)
}
}

extension UITableView {

func setCustomCellLabelFont(size: CGFloat) {
let labelAppearanceProxy = UILabel.appearance(whenContainedInInstancesOf: [UITableViewCell.self])
labelAppearanceProxy.font = UIFont.systemFont(ofSize: size)
}

}

成果

--

--