随着产品的不断发展,越来越多的公共功能可以封装成组件库,从而被各个模块甚者多个APP而使用,比如字体,间距,头像可以封装UI组件库,

  • 利用这些公共功能的组件库,不仅节约节省大量的开发时间,
  • 还能减少编译时间,因为如果没有独立的组件库一点点的代码改动都会导致整个App重新编译与连接

公共功能组件库

公共库的使用范围分类

内部库

该库和主项目共享一个Repo

私有库

该库使用独立的私有库Repo

开源库

该库发布到GitHub等开源社区提供给其他开发者使用

封装公共功能组件库

创建

检测

使用

设计UI组件库

间距库

import UIKit

public extension UIFont {
    static let designKit = DesignKitTypography()

    struct DesignKitTypography {
        public var display1: UIFont {
            scaled(baseFont: .systemFont(ofSize: 42, weight: .semibold), forTextStyle: .largeTitle, maximumFactor: 1.5)
        }

        public var display2: UIFont {
            scaled(baseFont: .systemFont(ofSize: 36, weight: .semibold), forTextStyle: .largeTitle, maximumFactor: 1.5)
        }

        public var title1: UIFont {
            scaled(baseFont: .systemFont(ofSize: 24, weight: .semibold), forTextStyle: .title1)
        }

        public var title2: UIFont {
            scaled(baseFont: .systemFont(ofSize: 20, weight: .semibold), forTextStyle: .title2)
        }

        public var title3: UIFont {
            scaled(baseFont: .systemFont(ofSize: 18, weight: .semibold), forTextStyle: .title3)
        }

        public var title4: UIFont {
            scaled(baseFont: .systemFont(ofSize: 14, weight: .regular), forTextStyle: .headline)
        }

        public var title5: UIFont {
            scaled(baseFont: .systemFont(ofSize: 12, weight: .regular), forTextStyle: .subheadline)
        }

        public var bodyBold: UIFont {
            scaled(baseFont: .systemFont(ofSize: 16, weight: .semibold), forTextStyle: .body)
        }

        public var body: UIFont {
            scaled(baseFont: .systemFont(ofSize: 16, weight: .light), forTextStyle: .body)
        }

        public var captionBold: UIFont {
            scaled(baseFont: .systemFont(ofSize: 14, weight: .semibold), forTextStyle: .caption1)
        }

        public var caption: UIFont {
            scaled(baseFont: .systemFont(ofSize: 14, weight: .light), forTextStyle: .caption1)
        }

        public var small: UIFont {
            scaled(baseFont: .systemFont(ofSize: 12, weight: .light), forTextStyle: .footnote)
        }
    }
}

private extension UIFont.DesignKitTypography {
    func scaled(baseFont: UIFont, forTextStyle textStyle: UIFont.TextStyle = .body, maximumFactor: CGFloat? = nil) -> UIFont {
        let fontMetrics = UIFontMetrics(forTextStyle: textStyle)

        if let maximumFactor = maximumFactor {
            let maximumPointSize = baseFont.pointSize * maximumFactor
            return fontMetrics.scaledFont(for: baseFont, maximumPointSize: maximumPointSize)
        }
        return fontMetrics.scaledFont(for: baseFont)
    }
}

头像组件

import UIKit

public extension UIImageView {
    func asAvatar(cornerRadius: CGFloat = 4) {
        clipsToBounds = true
        layer.cornerRadius = cornerRadius
    }
}

点赞按钮

import Foundation
import UIKit

public extension UIButton {
    func asStarFavoriteButton(pointSize: CGFloat = 18, weight: UIImage.SymbolWeight = .semibold, scale: UIImage.SymbolScale = .default, fillColor: UIColor = UIColor(hex: 0xf1c40f)) {
        let symbolConfiguration = UIImage.SymbolConfiguration(pointSize: pointSize, weight: weight, scale: scale)
        let starImage = UIImage(systemName: "star", withConfiguration: symbolConfiguration)
        setImage(starImage, for: .normal)

        let starFillImage = UIImage(systemName: "star.fill", withConfiguration: symbolConfiguration)
        setImage(starFillImage, for: .selected)

        tintColor = fillColor
        addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
    }

    func asHeartFavoriteButton(pointSize: CGFloat = 18, weight: UIImage.SymbolWeight = .semibold, scale: UIImage.SymbolScale = .default, fillColor: UIColor = UIColor(hex: 0xe74c3c)) {
        let symbolConfiguration = UIImage.SymbolConfiguration(pointSize: pointSize, weight: weight, scale: scale)
        let heartImage = UIImage(systemName: "heart", withConfiguration: symbolConfiguration)
        setImage(heartImage, for: .normal)

        let heartFillImage = UIImage(systemName: "heart.fill", withConfiguration: symbolConfiguration)
        setImage(heartFillImage, for: .selected)

        tintColor = fillColor
        addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
    }
}

private extension UIButton {
    @objc
    private func touchUpInside(sender: UIButton) {
        isSelected = !isSelected
    }
}

设计功能开关组件

请添加图片描述

定义了一个协议ToggleType,然后分别定义枚举类型代表三种不同的开关

protocol TogglesDataStoreType {
    func isToggleOn(_ toggle: ToggleType) -> Bool
    func update(toggle: ToggleType, value: Bool)
}

编译时开关

让编译器通过检查编译条件来启动或者关闭一些功能

import Foundation

enum BuildTargetToggle: ToggleType {
    case debug, `internal`, production
}

struct BuildTargetTogglesDataStore: TogglesDataStoreType {
    static let shared: BuildTargetTogglesDataStore = .init()

    private let buildTarget: BuildTargetToggle

    private init() {
        #if DEBUG
        buildTarget = .debug
        #endif

        #if INTERNAL
        buildTarget = .internal
        #endif

        #if PRODUCTION
        buildTarget = .production
        #endif
    }

    func isToggleOn(_ toggle: ToggleType) -> Bool {
        guard let toggle = toggle as? BuildTargetToggle else {
            return false
        }

        return toggle == buildTarget
    }

    func update(toggle: ToggleType, value: Bool) { }
}

由于不可能在运行时修改编译的条件,因此update方法实现体为空

INTERNAL 要通过xxconfig文件来配置

SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) INTERNAL

本地开关

让用户在app里面手动启动或者关闭一些功能

可以让测试人员和产品经理验证

远程开关

让后台关闭一些功能

封装UI组件的时候,遵循的原则

1.尽量使用扩展方法而不是子类来扩展组件,这样做可以使其他开发者在使用组件时,仅需要调用扩展方法,而不必使用特定的类
2.尽量使用代码而不要使用XIb或者Stroryboard,因为有些App完全不使用Interface Builder
3.如果可以,要为组件加上@IBDesignable 和 IBInspecatble支持,这样能够使的开发者在使用Interface Builder 的时候预览我们的组件。
4.尽量使用UIKit,而不要依赖任何第三方库,否则我们可能会引入一个不可控的依赖库

注意点

  • 1.组件设计要符合单一功能原则

一个组件只做一件事情,一个组件只做一类相关的事情,每个组件库都要有相对独立且功能单一。

  • 分别封装网络库,UI库,蓝牙处理库等底层库,
  • 但不能把所有的库合并在一个单独的库里面,这样可以方便上层应用按需使用这些依赖库。
  • 2.每次发布新增和更新组件库的时候都需要严格更新版本号
  • 3.根据业务需求吧公共模块一点点地移入公共组件库中,一步步的完善组件库的功能
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐