SwiftTweaks

无需重新编译,即可动态调整您的 iOS 应用!

SwiftTweaks Icon

您的用户不会看到您的动画研究、Sketch 组件或原型。他们将看到的是最终产品 - 因此确保您的应用在真实设备上感觉良好非常重要!

在笔记本电脑上看起来很棒的动画,在手中往往感觉太慢。在 27 英寸显示器上看起来完美的布局,在 4 英寸设备上可能过于拥挤。浅灰色文本在 Sketch 中看起来可能很微妙,但在阳光明媚的户外却完全难以辨认。

这些动画时间、字体大小和颜色选择都是“魔法数字”的例子 - 这些常量赋予您的应用可用性和特性。SwiftTweaks 的目标:允许您在 Swift 项目的调试版本中微调这些魔法数字,而无需等待 Xcode 重新构建应用。

Tweaks

SwiftPM compatible Carthage compatible Version GitHub release Swift 5.0 platforms Build Status

概述

在代码中使用 Tweak 代替布尔值、数字或颜色。您可以调整该 Tweak 而无需重新编译,这意味着您可以在没有 Xcode 的情况下调整动画时间、颜色和布局!

目前,您可以调整以下类型

一个 Tweak 看起来像这样

public static let colorTint = Tweak("General", "Colors", "Tint", UIColor.blueColor())

还有一些有用的 TweakGroupTemplate 类型,因此您可以快速声明常用的组合。它们都具有合理的默认值,但当然,您可以设置自己的值!

// Controls delay and duration for UIView.animate
// Use it with UIView.animate(basicTweakTemplate:...)
public static let basicAnimation = BasicAnimationTweakTemplate("Animation", "Basic Animation")

// Controls delay, duration, damping, and initial spring velocity for UIView.animate
// Use it with UIView.animate(springTweakTemplate:...)
public static let springAnimation = SpringAnimationTweakTemplate("Animation", "Spring Animation")

// Controls shadow color, radius, offset, and opacity for CALayer
// Use it with CALayer.apply(shadowTweakTemplate:...)
public static let shadowTweak = ShadowTweakTemplate("Shadows", "Button Shadow")

// Controls top/right/bottom/left for UIEdgeInsets
// Use it with UIEdgeInsets.init(edgeInsetsTweakTemplate)
public static let edgeInsets = EdgeInsetsTweakTemplate("Layout", "Screen Edge Insets")

当然,如果您愿意,您可以创建自己的 TweakGroupTemplate 类型 - 每当您有一组需要一起使用的 tweaks 以获得所需效果时,它们会很方便。 它们可以由任何 Tweak 组合构建。

Tweaks

操作

SwiftTweaks 现在支持闭包,因此您可以执行不依赖于 SwiftTweaks 中数据的操作。为此,请在您的 TweakStore 中使用 TweakAction 作为类型来创建一个执行自定义闭包的 Tweak。

public static let action = Tweak<TweakAction>("Actions", "Action", "Perform some action")

稍后在代码中,您可以将闭包添加到该 tweak,这些闭包在按下 Tweaks 窗口中的按钮时执行。

let idenfitier = ExampleTweaks.action.addClosure {
	/// Some complicated action happens here
	print("We're all done!")
}

如果您愿意,您也可以使用 addClosure 方法提供的唯一标识符来删除闭包。

ExampleTweaks.action.removeClosure(with: idenfitier)

等等,那 Facebook Tweaks 呢?

好问题!很高兴你问到。SwiftTweaks 存在的全部原因是因为我们非常喜欢 FBTweaks。 我们一直是 Objective-C 项目中 FBTweaks 的长期粉丝:用 FBTweak 宏替换魔法数字,就搞定了!您可以将 FBTweak 宏留在您的生产代码中,因为它在编译时会被 tweak 的默认值替换。

但是 Swift 不支持这种宏魔法,因此在 Swift 代码中使用 FBTweaks 很麻烦。我们的应用几乎都是 Swift 写的,所以我们想看看是否可以制作一些更容易的东西!

Tweaking 的步骤

将 SwiftTweaks 添加到您的项目有三个步骤

  1. 创建一个 TweakLibraryType,它包含一组 Tweak 和一个 TweakStore 来持久化它们。
  2. 在您的代码中引用该 TweakLibraryType 以使用 Tweak
  3. 在您的 AppDelegate 中,将 TweakWindow 作为您应用的窗口(还有其他选项,但这是最直接的!稍后会详细介绍。)

现在构建并运行,然后摇动您的手机以调出 Tweaks UI!调整 tweaks,当您对所获得的内容感到满意时,从 Tweaks UI 中与他人分享您的 tweaks。

第一步:创建您的 TweakLibrary

tweak 库负责列出许多 public static tweaks,并构建一个 TweakStore。tweak 库看起来像这样

public struct ExampleTweaks: TweakLibraryType {
	public static let colorTint = Tweak("General", "Colors", "Tint", UIColor.blue)
	public static let marginHorizontal = Tweak<CGFloat>("General", "Layout", "H. Margins", defaultValue: 15, min: 0)
	public static let marginVertical = Tweak<CGFloat>("General", "Layout", "V. Margins", defaultValue: 10, min: 0)
	public static let font = Tweak<StringOption>("General", "Layout", "Font", options: ["AvenirNext", "Helvetica", "SanFrancisco"])
	public static let featureFlagMainScreenHelperText = Tweak<Bool>("Feature Flags", "Main Screen", "Show Body Text", true)

	public static let buttonAnimation = SpringAnimationTweakTemplate("Animation", "Button Animation")

	public static let defaultStore: TweakStore = {
		let allTweaks: [TweakClusterType] = [colorTint, marginHorizontal, marginVertical, featureFlagMainScreenHelperText, buttonAnimation]

		let tweaksEnabled = TweakDebug.isActive

		return TweakStore(
			tweaks: allTweaks,
			enabled: tweaksEnabled
		)
	}()
}

让我们分解一下这里发生了什么

第二步:使用您的 TweakLibrary

要使用 tweak,您需要用 Tweak 引用替换代码中的数字或 UIColor,如下所示

这是我们的原始代码

button.tintColor = UIColor.green

assign 返回 tweak 的当前值

button.tintColor = ExampleTweaks.assign(ExampleTweaks.colorTint)

bind 立即调用其闭包,并在每次 tweak 更改时再次调用

ExampleTweaks.bind(ExampleTweaks.colorTint) { button.tintColor = $0 }

bindMultiple 立即调用其闭包,并在其任何 tweaks 更改时再次调用

// A "multipleBind" is called initially, and each time _any_ of the included tweaks change:
let tweaksToWatch: [TweakType] = [ExampleTweaks.marginHorizontal, ExampleTweaks.marginVertical]
ExampleTweaks.bindMultiple(tweaksToWatch) {
	let horizontal = ExampleTweaks.assign(ExampleTweaks.marginHorizontal)
	let vertical = ExampleTweaks.assign(ExampleTweaks.marginVertical)
	scrollView.contentInset = UIEdgeInsets(top: vertical, right: horizontal, bottom: vertical, left: horizontal)
}

有关更多示例,请查看示例项目的 ViewController.swift 文件。

第三步:将 TweakWindow 设置为您的根视图控制器

默认情况下,SwiftTweaks 使用摇晃手势来调出 UI,但您也可以使用自定义手势!

安装

Swift Package Manager

SwiftTweaks 可通过 Swift Package Manager 获得;使用此 URL 将其添加到 Xcode

https://github.com/Khan/SwiftTweaks

它也列在(优秀的)Swift Package Index 中!

Carthage

要将 SwiftTweaks 添加到您的应用程序,请将其添加到您的 Cartfile

github "Khan/SwiftTweaks"

此外,在项目的构建设置中为您的 _Debug_ 配置将 -DDEBUG 添加到**其他 Swift 标志**。

CocoaPods

pod 'SwiftTweaks'

# Enable DEBUG flag in Swift for SwiftTweaks
post_install do |installer|
    installer.pods_project.targets.each do |target|
        if target.name == 'SwiftTweaks'
            target.build_configurations.each do |config|
                if config.name == 'Debug'
                    config.build_settings['OTHER_SWIFT_FLAGS'] = '-DDEBUG'
                end
            end
        end
    end
end

FAQ

我_必须_将 TweakWindow 设置为我应用的根目录吗?

不!无论您喜欢在哪里/如何,只需创建一个 TweaksViewController,如下所示

let tweaksVC = TweaksViewController(tweakStore: ExampleTweaks.defaultStore, delegate: self)

我的应用中可以有多个 TweakLibraryType 吗?

当然!您可以使用唯一的 storeName 标识符初始化它们的 defaultStore,如下所示

public struct FirstTweaksLibrary: TweakLibraryType {
	// ...

	public static let defaultStore: TweakStore = {
		let allTweaks: [TweakClusterType] = //...

		return TweakStore(
			tweaks: allTweaks,
			storeName: "FirstTweaksLibrary", 	// Here's the identifier
			enabled: tweaksEnabled
		)
	}()
}

为什么不能对 Tweak 使用任何类型?

虽然 Tweak<T> 是通用的,但我们必须将 T 限制为 TweakableType,以便我们可以保证每种 T 都可以呈现在我们的编辑界面中并持久保存在磁盘上。 更多类型会很棒! 支持字典、闭包和其他东西会很棒。

如果您想扩展 TweakableType,您需要扩展一些内部组件,如 TweakViewDataTypeTweakDefaultDataTweakViewDataTweakPersistency。 如果您想添加新类型,请随时打开 pull request!

如何创建新的 TweakGroupTemplate?

也许您正在使用不同的动画框架,或者想要一个用于 CGRect 或类似的东西的模板 - 太棒了! 只要模板的可调整“组件”符合 TweakableType,一切就绪。 创建一个新的 TweakGroupTemplateType,并查看现有模板以获取实现建议。(您可能也想使用 SignedNumberTweakDefaultParameters - 它们非常有用!)

如果您认为您的 TweakGroupTemplateType 会帮助其他人,请提交 pull request!