UtiliKit 1.8.0

UtiliKit 1.8.0

Will McGintyTyler MilnerEarl GaspardRyan Gant维护。



UtiliKit 1.8.0

  • Bottle Rocket Studios

UtiliKit

CI Status Version Carthage compatible License Platform codecov codebeat badge

目的

这个库为iOS应用提供了几个有用且常见的添加功能。这些扩展、协议和结构体旨在简化样板代码,以及移除常见的字符串类型用法。

关键概念

这个库分为7个部分,作为CocoaPods子规范可用。

  • 实例化 - 这个子规范将"字符串类型"视图实例化、视图控制器实例化和可重用视图出队列更改为安全类型函数调用。
  • 通用 - 这个子规范包括对FileManagerUIView的扩展。这简化了获取常见URL和通过编程添加视图到简单的变量和函数调用。
  • 版本 - 这个子规范简化了版本和构建号的显示。
  • TimelessDate - 这个子规范是对DateCalendar的抽象。它主要是为简单调度和每天比较设计的,其中时间不如实际日期重要。
  • Container - 这个子规范提供了一个简单的`ContainerViewController`,没有任何内置的导航结构。
  • ActiveLabel - 这个子规范提供了一个`UILabel`子类,当`text`属性设置为`nil`时,它渲染渐变"加载"动画。
  • Obfuscation - 这个子规范提供了一些简单的例行程序,可以从源代码中删除明文密码或密钥。

使用

实例化

可重用视图

注册和出队单元格、收集视图辅助视图、表格视图的头部和尾部以及注释就像调用它们的展示视图上的register方法,以及在collectionView(_:cellForItemAt:) -> UICollectionViewCell或等效功能中出队它们一样简单。

class ViewController: UIViewController {
    @IBOutlet var collectionView: UICollectionView!
    let dataA: [Int] = [0, 1, 2]
    let dataB: [Int] = [0, 1, 2]
    let dataC: [Int] = [0, 1, 2]
    var data: [[Int]] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView.register(ProgrammaticCell.self)
        collectionView.registerHeaderFooter(ProgrammaticHeaderFooterView.self)
        collectionView.delegate = self
        collectionView.dataSource = self
    }
}

// MARK: - UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {

    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return data[section].count
    }

    public func numberOfSections(in collectionView: UICollectionView) -> Int {
        return data.count
    }

    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // Dequeue a "ProgrammaticCell" from the collection view using only the cell type
        let cell: ProgrammaticCell = collectionView.dequeueReusableCell(for: indexPath)
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

        // You need only provide the desired type and SupplementaryElementKind to receive a typed UICollectionReusableView
        switch kind {
        case UICollectionElementKindSectionHeader:
            let header: ProgrammaticHeaderFooterView = collectionView.dequeueReusableSupplementaryView(of: .sectionHeader, for: indexPath)
            return header
        default:
            let footer: ProgrammaticHeaderFooterView = collectionView.dequeueReusableSupplementaryView(of: .sectionFooter, for: indexPath)
            footer.kind = .sectionFooter
            return footer
        }
    }
}

// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.bounds.width, height: 100)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize(width: collectionView.bounds.width, height: 50)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
        return CGSize(width: collectionView.bounds.width, height: 25)
    }
}

视图控制器

为了从一个Storyboard中实例化一个视图控制器,您只需创建一个Storyboard.Identifier,并定义返回类型。一个简单的实现可能如下所示

extension UIStoryboard.Identifier {

    static let myStoryboard = UIStoryboard.Identifier(name: "MyStoryboard")
}

class ViewController: UIViewController {

    func presentMyViewController() {
        let vc: MyViewController = UIStoryboard(identifier: .myStoryboard).instantiateViewController()
        present(vc, animated: true)
    }
}

通用

文件管理器扩展

FileManager上提供了几个便利方法作为扩展

let documentsDirectory = FileManager.default.documentsDirectory
let cachesDirectory = FileManager.default.cachesDirectory
let appSupportDirectory = FileManager.default.applicationSupportDirectory
let sharedContainerURL = FileManager.default.sharedContainerURL(forSecurityApplicationGroupIdentifier: "com.app.group")

UIView 扩展

UIView 上提供了一些便利方法作为扩展,主要用于轻松地将子视图约束到父视图中

let myView = UIView(frame: .zero)
view.addSubview(myView, constrainedToSuperview: true)

let anotherView = UIView(frame: .zero)
anotherView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(anotherView)

let insets = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)
anotherView.constrainEdgesToSuperview(with: insets)

版本号

将版本号转换为用户视图字符串只需调用一个函数。*注意:如果提供的版本配置包含无效键,该函数将抛出错误。一个简单的实现可能如下所示

func printVersions() {
    do {
        let customVersionString = try Bundle.main.versionString(for: MyVersionConfig(), isShortVersion: false)
        let verboseVersionString = try Bundle.main.verboseVersionString()
        let versionString = try Bundle.main.versionString()

        print(customVersionString)
        print(verboseVersionString)
        print(versionString)
    } catch {
        print(error)
    }
}

永恒日期

永恒日期是一个非常简单的抽象,它从日期中移除时间,并使用日历进行计算。这对于日历和旅行用例非常有用,因为了解两个事件之间有多少天通常比两者之间的小时数 / 24 更重要。

func numberOfDaysBetween(start: TimelessDate, finish: TimelessDate) -> DateInterval {
    return start.dateIntervalSince(finish)
}

func isOneWeekFrom(checkout: TimelessDate) -> Bool {
    return checkout.dateIntervalSince(TimelessDate()) <= 7
}

这个结构还移除了给日期添加天数、小时数、分钟数和秒数的模糊计算,并用日历计算替换。

func addOneHourTo(date: Date) -> Date {
    return date.adding(hours: 1)
}

容器视图控制器

用于管理多个子视图控制器的解决方案,ContainerViewController 管理子控制器的生命周期。这允许您专注于视图的导航结构以及它们之间的过渡。

containerViewController.managedChildren = [Child(identifier: "A", viewController: controllerA), Child(identifier: "B", viewController: controllerB)]

containerViewController.willMove(toParent: self)

addChild(containerViewController)
containerView.addSubview(containerViewController.view)
containerViewController.view.frame = containerView.bounds

containerViewController.didMove(toParent: self)

在此阶段,在容器的子视图之间进行切换非常简单。

let child = ...
containerViewController.transitionToController(for: child)

容器还具有几个委托回调,可以帮助自定义其行为。其中之一是一个返回 UIViewControllerAnimatedTransitioning 对象的函数。

func containerViewController(_ container: ContainerViewController, animationControllerForTransitionFrom source: UIViewController, to destination: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if useCustomAnimator, let sourceIndex = container.index(ofChild: source), let destinationIndex = container.index(ofChild: destination) {
        return WipeTransitionAnimator(withStartIndex: sourceIndex, endIndex: destinationIndex)
    }

    return nil
}

ActiveLabel

ActiveLabel 是一个继承自 UILabel 的子类,在设置其 text 属性为 nil 时向您的标签添加水平进度指示器。您可以通过代码或在 Interface Builder 中自定义该视图,以适应您的特定需求。此子类的主要目的是在您向标签中加载数据时,在标签级别提供视觉指示。

默认配置

let label: ActiveLabel = ActiveLabel()

自定义配置

let label: ActiveLabel = ActiveLabel()
label.estimatedNumberOfLines = 3
label.finalLineTrailingInset = 100

使用便利初始化器进行自定义配置。

var configuration = ActiveLabelConfiguration.default
configuration.estimatedNumberOfLines = 3
configuration.finalLineLength = 100
configuration.loadingView.animationDuration = 2.0
configuration.loadingView.animationDelay = 0
let label: ActiveLabel = ActiveLabel(frame: CGRect(x: 0, y: 0, width: 335, height: 21), configuration: configuration)

添加一些颜色,更改行高和间距。

let label: ActiveLabel = ActiveLabel()
label.estimatedNumberOfLines = 3
label.finalLineTrailingInset = 100
label.loadingView.color = UIColor(red: 233.0/255.0, green: 231.0/255.0, blue: 237.0/255.0, alpha: 1.0))
label.loadingView.lineHeight = 16
label.loadingView.lineVerticalSpacing = 8

在 Storyboards 或 Xibs 中初始化 ActiveLabel 时,必须在代码中将标签的文本设置为 nil,因为 IB 以空字符串的值初始化标签。

在用于快照测试的 ActiveLabel 中,您可以通过在您的标签上调用 configureForSnapshotTest() 来使渐变居中。

滚动页面控制器

ScrollingPageControl 是一个基于 Apple 的 UIPageControl(但不继承自)构建的视图。该类旨在允许在有限的空间内表示大量的页面,并提供比 UIPageControl 更多的自定义选项。

默认配置,与 UIPageControl 的相似之处

let pageControl: ScrollingPageControl = ScrollingPageControl()
pageControl.numberOfPages = 30                          // default is 0
pageControl.currentPage = 14                            // default is 0
pageControl.hidesForSinglePage = false                  // default
pageControl.pageIndicatorTintColor = .systemGray        // default
pageControl.currentPageIndicatorTintColor = .systemBlue // default

自定义点布局

pageControl.mainDotCount = 5                           // default is 3
pageControl.marginDotCount = 3                         // default is 2
pageControl.dotSize = CGSize(width: 5.0, height: 10.0) // default is 7.0 x 7.0
pageControl.dotSpacing = 14.0                          // default is 9.0
pageControl.minimumDotScale = 0.25                     // default is 0.4

响应滚动页面控制器交互

pageControl.didSetCurrentPage = { [weak self] (index) in
    self?.scrollToPageAtIndex(index)
}

添加自定义页面点

pageControl.customPageDotAtIndex = { [weak self] (index) in
    guard self?.pageData[index].isFavorited else { return nil }
    return FavoriteIconView()
}

使用说明

  • customPageDotAtIndex 块中返回对 indexnil 将默认到指定索引的 dotSize 的标准页面点。
  • 建议从该块返回的任何自定义视图都应对 tintColorDidChange() 做出响应,以清楚地表明它是否是 currentPage
  • 建议从该块返回的任何自定义视图都应该考虑到 dotSizedotSpacing,以保持一致的外观和感觉。
  • 在任何使用该块的数据在它首次设置后更新时,应调用 updateDot(at:)updateDots(at:) 以保持页面控制器同步。

匿名密钥

要在代码中使用匿名密钥,创建一个并使用构建器变量来编码您的密钥。

let key = ObfuscatedKey().T.h.i.s.underscore.I.s.dash.o.b.f.u.s.c.a.t.e.d.value

示例

要运行示例项目,请克隆仓库,打开 UtiliKit.xcworkspace,然后运行 "UtiliKit-iOSExample" 项目。

需求

  • iOS 10.0+
  • Swift 5.0

安装 - Swift Package Manager

dependencies: [
    .package(url: "https://github.com/BottleRocketStudios/iOS-UtiliKit.git", from: "1.6.0")
]

然后,您需要从可用的库中选择要添加到项目中的库。这些库应与通过CocoaPods提供的子spec相匹配。

  • UtiliKit - 导入以下所有可用库。
  • GeneralUtilities - 此子spec包括对FileManagerUIView的扩展。这些简化了获取常见URL和通过简单变量和函数调用程序添加视图的过程。
  • Instantiation - 此子spec将“Stringly-typed”视图实例化、视图控制器实例化和可重用视图提取转换为安全的函数调用。
  • TimelessDate - 此子spec是针对DateCalendar的抽象。它主要设计用于简单的调度和日比较,其中时间比实际日子更重要。
  • Versioning - 此子spec简化了版本和构建号显示。
  • ContainerViewController - 此子spec提供不含任何内置导航结构的简单ContainerViewController
  • ActiveLabel - 此子spec提供负责在标签的text属性设置为nil时渲染渐变“加载”动画的UILabel子类。
  • Obfuscation - 此子spec提供简单的例程,用于从源代码中移除明文密码或密钥。

安装 - CocoaPods

将以下内容添加到您的 Podfile

pod 'UtiliKit'

请确保您已选择使用框架

use_frameworks!

然后使用CocoaPods的0.36或更新版本运行 pod install

安装 - Carthage

将以下内容添加到您的 Cartfile

github "BottleRocketStudios/iOS-UtiliKit"

运行 carthage update 并按照Carthage的README中描述的步骤进行。

用户贡献

查看用户贡献文档。感谢,贡献者