Skip to content

Commit

Permalink
Optimize FluentTheme updating (#1976)
Browse files Browse the repository at this point in the history
* New API, `isApplicableThemeChange(_:for:)`

* Update views to use new API for FluentTheme updates

* Fix bug in theme setting for `DemoListViewController`

* Updating visibility and comment for `FluentTheme.shared`
  • Loading branch information
mischreiber authored Feb 17, 2024
1 parent 1d84a47 commit 30393f0
Show file tree
Hide file tree
Showing 13 changed files with 53 additions and 35 deletions.
14 changes: 8 additions & 6 deletions ios/FluentUI.Demo/FluentUI.Demo/BrandedSwitch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ class BrandedSwitch: UISwitch {
override init(frame: CGRect) {
super.init(frame: frame)

NotificationCenter.default.addObserver(forName: .didChangeTheme,
object: nil,
queue: nil) { [weak self] notification in
notificationObserver = NotificationCenter.default.addObserver(forName: .didChangeTheme,
object: nil,
queue: nil) { [weak self] notification in
guard let strongSelf = self,
let themeView = notification.object as? UIView,
strongSelf.isDescendant(of: themeView)
FluentTheme.isApplicableThemeChange(notification, for: strongSelf)
else {
return
}
strongSelf.onTintColor = themeView.fluentTheme.color(.brandForeground1)
strongSelf.onTintColor = strongSelf.fluentTheme.color(.brandForeground1)
}
}

Expand All @@ -34,4 +33,7 @@ class BrandedSwitch: UISwitch {
}
onTintColor = newWindow.fluentTheme.color(.brandForeground1)
}

/// Stores the notification handler for .didChangeTheme notifications.
private var notificationObserver: NSObjectProtocol?
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ColoredPillBackgroundView: UIView {
}

@objc func themeDidChange(_ notification: Notification) {
guard let themeView = notification.object as? UIView, self.isDescendant(of: themeView) else {
guard FluentTheme.isApplicableThemeChange(notification, for: self) else {
return
}
updateBackgroundColor()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class DemoListViewController: DemoTableViewController {
func updateColorProviderFor(window: UIWindow, theme: DemoColorTheme) {
self.theme = theme
if let provider = self.provider {
FluentTheme.setSharedThemeColorProvider(provider)
window.setColorProvider(provider)
let fluentTheme = self.view.fluentTheme
let primaryColor = fluentTheme.color(.brandBackground1)
FluentUIFramework.initializeAppearance(with: primaryColor, whenContainedInInstancesOf: [type(of: window)])
Expand Down
2 changes: 1 addition & 1 deletion ios/FluentUI/Calendar/CalendarView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class CalendarView: UIView {
}

@objc private func themeDidChange(_ notification: Notification) {
guard let themeView = notification.object as? UIView, self.isDescendant(of: themeView) else {
guard FluentTheme.isApplicableThemeChange(notification, for: self) else {
return
}
updateCollectionViewBackgroundColor()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class CalendarViewWeekdayHeadingView: UIView {
}

@objc private func themeDidChange(_ notification: Notification) {
guard let themeView = notification.object as? UIView, self.isDescendant(of: themeView) else {
guard FluentTheme.isApplicableThemeChange(notification, for: self) else {
return
}
updateBackgroundColor()
Expand Down
2 changes: 1 addition & 1 deletion ios/FluentUI/Core/ControlHostingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ open class ControlHostingView: UIView {
}

@objc private func themeDidChange(_ notification: Notification) {
guard let themeView = notification.object as? UIView, self.isDescendant(of: themeView) else {
guard FluentTheme.isApplicableThemeChange(notification, for: self) else {
return
}
updateRootView()
Expand Down
49 changes: 33 additions & 16 deletions ios/FluentUI/Core/Theme/FluentTheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,26 +83,43 @@ public class FluentTheme: NSObject, ObservableObject {
gradientTokenSet: gradientTokenSet)
}()

/// A shared, immutable, default `FluentTheme` instance.
/// The shared `FluentTheme` instance used by default for controls in the app.
///
/// This instance of `FluentTheme` is not customizable, and will not return any overridden values that may be
/// applied to other instances of `FluentTheme`. For example, any branding colors applied via an instantiation of
/// the `ColorProviding` protocol will not be reflected here. As such, this should only be used in cases where the
/// caller is certain that they are looking for the _default_ token values associated with Fluent.
/// This static `FluentTheme` instance will normally return the default token values associated
/// with Fluent. However, it is also available for overriding in cases where a single custom theme
/// is desired for the app linking this library.
///
/// Note that any custom themes set on a `UIView` hierarchy or via a SwiftUI view modifier will
/// take precedence over this value. This value provides the fallback theme for cases where those
/// overrides are not provided.
@objc(sharedTheme)
public internal(set) static var shared: FluentTheme = FluentThemeKey.defaultValue {
public static var shared: FluentTheme = FluentThemeKey.defaultValue {
didSet {
UIApplication.shared.connectedScenes
.compactMap {
$0 as? UIWindowScene
}
.flatMap {
$0.windows
}
.forEach { window in
NotificationCenter.default.post(name: .didChangeTheme, object: window)
}
NotificationCenter.default.post(name: .didChangeTheme, object: nil)
}
}

/// Determines if a given `Notification` should cause an update for the given `UIView`.
///
/// - Parameter notification: A `Notification` object that may be requesting a view update based on a theme change.
/// - Parameter view: The `UIView` instance that wants to determine whether to update.
///
/// - Returns: `True` if the view should update, `false` otherwise.
@objc(isApplicableThemeChangeNotification:forView:)
public static func isApplicableThemeChange(_ notification: Notification,
for view: UIView) -> Bool {
// Do not update unless the notification's name is `.didChangeTheme`.
guard notification.name == .didChangeTheme else {
return false
}

// If there is no object, or it is not a UIView, we must assume that we need to update.
guard let themeView = notification.object as? UIView else {
return true
}

// If the object is a UIView, we only update if `view` is a descendant thereof.
return view.isDescendant(of: themeView)
}

// Token storage
Expand Down
5 changes: 2 additions & 3 deletions ios/FluentUI/Core/Theme/Tokens/ControlTokenSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,12 @@ public class ControlTokenSet<T: TokenSetKey>: ObservableObject {
object: nil,
queue: nil) { [weak self, weak control] notification in
guard let strongSelf = self,
let themeView = notification.object as? UIView,
let control,
control.isDescendant(of: themeView)
FluentTheme.isApplicableThemeChange(notification, for: control)
else {
return
}
strongSelf.update(themeView.fluentTheme)
strongSelf.update(control.fluentTheme)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class DatePickerController: UIViewController, GenericDateTimePicker {
}

@objc private func themeDidChange(_ notification: Notification) {
guard let themeView = notification.object as? UIView, view.isDescendant(of: themeView) else {
guard FluentTheme.isApplicableThemeChange(notification, for: view) else {
return
}
updateBackgroundColor()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class DateTimePickerController: UIViewController, GenericDateTimePicker {
}

@objc private func themeDidChange(_ notification: Notification) {
guard let themeView = notification.object as? UIView, view.isDescendant(of: themeView) else {
guard FluentTheme.isApplicableThemeChange(notification, for: view) else {
return
}
updateBackgroundColor()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class DateTimePickerView: UIControl {
}

@objc private func themeDidChange(_ notification: Notification) {
guard let themeView = notification.object as? UIView, self.isDescendant(of: themeView) else {
guard FluentTheme.isApplicableThemeChange(notification, for: self) else {
return
}
updateBackgroundColor()
Expand Down
2 changes: 1 addition & 1 deletion ios/FluentUI/People Picker/PersonaListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ open class PersonaListView: UITableView {
}

@objc private func themeDidChange(_ notification: Notification) {
guard let themeView = notification.object as? UIView, self.isDescendant(of: themeView) else {
guard FluentTheme.isApplicableThemeChange(notification, for: self) else {
return
}
updateBackgroundColor()
Expand Down
2 changes: 1 addition & 1 deletion ios/FluentUI/Presenters/PageCardPresenterController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ open class PageCardPresenterController: UIViewController {
}

@objc private func themeDidChange(_ notification: Notification) {
guard let themeView = notification.object as? UIView, view.isDescendant(of: themeView) else {
guard FluentTheme.isApplicableThemeChange(notification, for: view) else {
return
}
updatePageControlColors()
Expand Down

0 comments on commit 30393f0

Please sign in to comment.