• Stars
    star
    243
  • Rank 166,489 (Top 4 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A powerful and lightweight and customization theme/skin library for iOS 9+ in swift. 主题、换肤、暗黑模式

中文文档

JXTheme is a lightweight library for theme properties configuration.

Feature

  • Support for iOS 9+, let your app implement DarkMode earlier;
  • Use the theme namespace attribute: view.theme.xx = xx. Say goodbye to the theme_xx attribute extension usage;
  • ThemeStyle can be customized by extension, no longer limited to light and dark;
  • provides the customization attribute as a callback entry for theme switching, with the flexibility to configure any property. It is no longer limited to the provided attributes such as backgroundColor and textColor;
  • supports the control setting overrideThemeStyle, which affects its child views;

Preview

preview

Requirements

  • iOS 9.0+
  • XCode 10.2.1+
  • Swift 5.0+

Install

Manual

Clone code, drag the Sources folder into the project, you can use it;

CocoaPods

Target '<Your Target Name>' do
     Pod 'JXTheme'
End

Execute pod repo update first, then execute pod install

Carthage

Add in the cartfile:

Github "pujiaxin33/JXTheme"

Then execute carthage update --platform iOS. For other configurations, please refer to the Carthage documentation.

#Usage

Add a custom style by extensionThemeStyle

ThemeStyle only provides a default unspecified style. Other business styles need to be added by themselves. For example, only light and dark are supported. The code is as follows:

Extension ThemeStyle {
    Static let light = ThemeStyle(rawValue: "light")
    Static let dark = ThemeStyle(rawValue: "dark")
}

Basic use

view.theme.backgroundColor = ThemeProvider({ (style) in
    If style == .dark {
        Return .white
    }else {
        Return .black
    }
})
imageView.theme.image = ThemeProvider({ (style) in
    If style == .dark {
        Return UIImage(named: "catWhite")!
    }else {
        Return UIImage(named: "catBlack")!
    }
})

Custom Properties Configuration

If the library does not natively support a certain attribute, it can be handled uniformly in the customization.

View.theme.customization = ThemeProvider({[weak self] style in
    / / You can choose any other property
    If style == .dark {
        Self?.view.bounds = CGRect(x: 0, y: 0, width: 30, height: 30)
    }else {
        Self?.view.bounds = CGRect(x: 0, y: 0, width: 80, height: 80)
    }
})

extension ThemeWrapper add property

If a certain attribute is frequently used in the project, and it is troublesome to use the above Custom Properties Configuration, you can add the desired property by yourself with the extension ThemeWrapper. (Ps: You can also submit a Pull Request to add)

The following is an example of UILabel adding shadowColor:

//Custom add ThemeProperty, currently only supports UIView, CALayer, UIBarItem and their subclasses
extension ThemeWrapper where Base: UILabel {
    var shadowColor: ThemeProvider<UIColor>? {
        set(new) {
            let baseItem = self.base
            ThemeTool.setupViewThemeProperty(view: self.base, key: "UILabel.shadowColor", provider: new) {[weak baseItem] (style) in
                baseItem?.shadowColor = new?.provider(style)
            }
        }
        get {return ThemeTool.getThemeProvider(target: self.base, with: "UILabel.shadowColor") as? ThemeProvider<UIColor>}
    }
}

The call is still the same:

//Custom attribute shadowColor
shadowColorLabel.shadowOffset = CGSize(width: 0, height: 2)
shadowColorLabel.theme.shadowColor = ThemeProvider({ style in
    if style == .dark {
        return .red
    }else {
        return .green
    }
})

Configuring the package example

JXTheme is a lightweight base library that provides configuration of theme properties, and does not restrict which way to load resources. The three examples provided below are for reference only.

ThemeProvider custom initializer

For example, add the following code to the project:

extension ThemeProvider {
     //Adjust according to the ThemeStyle supported by the project
     init(light: T, dark: T) {
         self.init {style in
             switch style {
             case .light: return light
             case .dark: return dark
             default: return light
             }
         }
     }
}

Call in business code:

tableView.theme.backgroundColor = ThemeProvider(light: UIColor.white, dark: UIColor.white)

In this way, the form of ThemeProvider closure can be avoided and it is more concise.

General Configuration Package Example

There is a UI standard for general skinning needs. For example, UILabel.textColor defines three levels, the code is as follows:

Enum TextColorLevel: String {
    Case normal
    Case mainTitle
    Case subTitle
}

Then you can encapsulate a global function and pass TextColorLevel to return the corresponding configuration closure, which can greatly reduce the amount of code during configuration. The global functions are as follows:

Func dynamicTextColor(_ level: TextColorLevel) -> ThemeProvider<UIColor> {
    Switch level {
    Case .normal:
        Return ThemeProvider({ (style) in
            If style == .dark {
                Return UIColor.white
            }else {
                Return UIColor.gray
            }
        })
    Case .mainTitle:
        ...
    Case .subTitle:
        ...
    }
}

The code for configuring the theme properties is as follows:

themeLabel.theme.textColor = dynamicTextColor(.mainTitle)

Local Plist file configuration example

Same as General Configuration Package, except that the method loads the configuration value from the local Plist file. The specific code participates in the Example``StaticSourceManager class.

Add topics based on server dynamics

Same as General Configuration Package, except that the method loads the specific values ​​of the configuration from the server. The specific code participates in the DynamicSourceManager class of Example.

Stateful controls

Some business requirements exist for a control with multiple states, such as checked and unchecked. Different states have different configurations for different theme. The configuration code is as follows:

statusLabel.theme.textColor = ThemeProvider({[weak self] (style) in
    If self?.statusLabelStatus == .isSelected {
        / / selected state a configuration
        If style == .dark {
            Return .red
        }else {
            Return .green
        }
    }else {
        //Unselected another configuration
        If style == .dark {
            Return .white
        }else {
            Return .black
        }
    }
})

When the state of the control is updated, you need to refresh the current theme property configuration, the code is as follows:

Func statusDidChange() {
    statusLabel.theme.textColor?.refresh()
}

If your control supports multiple state properties, such as textColor, backgroundColor, font, etc., you can call the refresh method without using one of the theme properties. You can use the following code to complete all the configured themes. Property refresh:

Func statusDidChange() {
    statusLabel.theme.refresh()
}

overrideThemeStyle

Regardless of how the theme switches, overrideThemeStyleParentView and its subview's themeStyle are dark

overrideThemeStyleParentView.theme.overrideThemeStyle = .dark

Principle

Other tips

Why use the theme namespace attribute instead of the theme_xx extension attribute?

  • If you extend N functions to the system class, when you use the class, there are N extended methods that interfere with your choice. Especially if you are doing other business development, not when you want to configure theme properties.
  • Well-known three-party libraries like Kingfisher, SnapKit, etc., all use namespace attributes to implement extensions to system classes. This is a more Swift way of writing and worth learning.

Theme Switch Notification

Extension Notification.Name {
    Public static let JXThemeDidChange = Notification.Name("com.jiaxin.theme.themeDidChangeNotification")
}

ThemeManager stores the theme configuration according to the user ID

/// Configure the stored flag key. Can be set to the user's ID, so that in the same phone, you can record the configuration of different users. You need to set this property first and then set other values.
Public var storeConfigsIdentifierKey: String = "default"

Migrating to System API Guide

When your app supports iOS13 at the minimum, you can migrate to the system plan if you need to follow the guidelines below. [Migrate to System API Guide, click to read] (https://github.com/pujiaxin33/JXTheme/blob/master/Document/%E8%BF%81%E7%A7%BB%E5%88%B0%E7% B3%BB%E7%BB%9FAPI%E6%8C%87%E5%8D%97.md)

Currently supported classes and their properties

The properties here are inherited. For example, UIView supports the backgroundColor property, then its subclass UILabel also supports backgroundColor. If you don't have the class or property you want to support, you are welcome to extend the PullRequest.

UIView

  • backgroundColor
  • tintColor
  • alpha
  • customization

UILabel

  • font
  • textColor
  • shadowColor
  • highlightedTextColor
  • attributedText

UIButton

  • func setTitleColor(_ colorProvider: ThemeColorDynamicProvider?, for state: UIControl.State)
  • func setTitleShadowColor(_ colorProvider: ThemeColorDynamicProvider?, for state: UIControl.State)
  • func setAttributedTitle(_ textProvider: ThemeAttributedTextDynamicProvider?, for state: UIControl.State)
  • func setImage(_ imageProvider: ThemeImageDynamicProvider?, for state: UIControl.State)
  • func setBackgroundImage(_ imageProvider: ThemeImageDynamicProvider?, for state: UIControl.State)

UITextField

  • font
  • textColor
  • attributedText
  • attributedPlaceholder
  • keyboardAppearance

UITextView

  • font
  • textColor
  • attributedText
  • keyboardAppearance

UIImageView

  • image

CALayer

  • backgroundColor
  • borderColor
  • borderWidth
  • shadowColor
  • customization

CAShapeLayer

  • fillColor
  • strokeColor

UINavigationBar

  • barStyle
  • barTintColor
  • titleTextAttributes
  • largeTitleTextAttributes

UITabBar

  • barStyle
  • barTintColor
  • shadowImage

UISearchBar

  • barStyle
  • barTintColor
  • keyboardAppearance

UIToolbar

  • barStyle
  • barTintColor

UISwitch

  • onTintColor
  • thumbTintColor

UISlider

  • thumbTintColor
  • minimumTrackTintColor
  • maximumTrackTintColor
  • minimumValueImage
  • maximumValueImage

UIRefreshControl

  • attributedTitle

UIProgressView

  • progressTintColor
  • trackTintColor
  • progressImage
  • trackImage

UIPageControl

  • pageIndicatorTintColor
  • currentPageIndicatorTintColor

UIBarItem

  • func setTitleTextAttributes(_ attributesProvider: ThemeAttributesDynamicProvider?, for state: UIControl.State)
  • image

UIBarButtonItem

  • tintColor

UIActivityIndicatorView

  • style
  • color

UIScrollView

  • indicatorStyle

UITableView

  • separatorColor
  • sectionIndexColor
  • sectionIndexBackgroundColor

Contribution

If you have any questions or suggestions, please feel free to contact us by Issue and Pull Request🤝

More Repositories

1

JXCategoryView

A powerful and easy to use category view (segmentedcontrol, segmentview, pagingview, pagerview, pagecontrol) (腾讯新闻、今日头条、QQ音乐、网易云音乐、京东、爱奇艺、腾讯视频、淘宝、天猫、简书、微博等所有主流APP分类切换滚动视图)
Objective-C
6,026
star
2

JXPagingView

类似微博主页、简书主页等效果。多页面嵌套,既可以上下滑动,也可以左右滑动切换页面。支持HeaderView悬浮、支持下拉刷新、上拉加载更多。
Objective-C
2,824
star
3

JXSegmentedView

A powerful and easy to use segmented view (segmentedcontrol, pagingview, pagerview, pagecontrol, categoryview) (腾讯新闻、今日头条、QQ音乐、网易云音乐、京东、爱奇艺、腾讯视频、淘宝、天猫、简书、微博等所有主流APP分类切换滚动视图)
Swift
2,596
star
4

JXPageListView

高仿闲鱼、转转、京东、中央天气预报等主流APP列表底部分页滚动视图
Objective-C
421
star
5

JXMarqueeView

A powerful and easy to use marquee view.
Swift
365
star
6

JXBottomSheetView

A useful and gesture interaction BottomSheetView!
Swift
256
star
7

JXPatternLock

An easy-to-use, powerful, customizable pattern lock view in swift. 图形解锁/手势解锁 / 手势密码 / 图案密码 / 九宫格密码
Swift
219
star
8

JXMovableCellTableView

The custom tableView which can start moving the cell with a long press gesture.
Objective-C
188
star
9

JXPopupView

一个轻量级的自定义视图弹出框架
Swift
139
star
10

StackUI

StackUI just like SwiftUI
Swift
117
star
11

JXScratchView

一个万能的刮刮乐控件。无论是UILabel、UIImageView,还是自定义视图,只要是UIView都可以用来刮。代码简单,功能强大,你值得拥有!
Swift
111
star
12

JXWeChatFloatView

高仿微信文章悬浮球
Swift
100
star
13

JXBottomSheetTableView

A highly packaged, easy to use custom bottom sheet UITableView.
Swift
48
star
14

JXExcel

一个轻量级的表视图
Swift
31
star
15

JXTableViewZoomHeaderImageView

一般app的个人资料页面都会有个头图,并且随着上下滚动的时候,图片有个移动和缩放的效果。
Objective-C
29
star
16

ModelAdapter

Simple JSON Object mapping and SQLite3 written in Swift
Swift
26
star
17

JXCaptain

像美国队长一样威猛的应用调试工具箱!
Swift
22
star
18

JXFileBrowserController

The debug sandbox browser for sharing.
Swift
22
star
19

JXGradientKit

常用控件背景渐变色Kit
Swift
19
star
20

JXLayerAutoLayout

优雅的实现CALayer AutoLayout
Objective-C
19
star
21

SQLiteValueExtension

SQLiteValueExtension for SQLite.swift
Swift
14
star
22

JXParallaxCell

用于滚动scrollview的时候,cell中的图片,或者其他控件,有一个视差滚动的效果
Objective-C
14
star
23

JXTransition

自定义转场动画
Objective-C
11
star
24

JXBorderCellll

一个有边框的基类cell
Objective-C
11
star
25

JXInterview

Interview for iOSer
10
star
26

XPackage

自己常用的一些工具
Objective-C
4
star
27

30DaysChallenge

There may be a miracle happen if you insist 30 days challenge!
Objective-C
3
star
28

JXExampleImages

示例图片资源
2
star
29

JXKit

JXKit for iOS
Swift
1
star
30

OnePiece

Swift
1
star
31

JXRomanticAlbum

谁说程序员不浪漫?我们可以用代码表白!Who says programmers aren't romantic? We can use the code to express love!
Swift
1
star