Swift   DocC   Artifactory

CombineUI

适用于 UI 手势、控件和视图的 Swift 属性包装器、绑定和 Combine 发布者

概述

CombineUI 为诸如手势、控件和视图等选定的 Apple 类型提供 Swift 属性包装器。 这些属性包装器 投影 Combine 发布者,用于手势识别、控件事件、属性更改和委托回调。

示例

@Button var button = UIButton()

$button 投影值是一个发布者,当按钮被点击时会发送。

并提供绑定,用于使用来自 Combine 发布者的值更新选定的 Apple 视图的属性。

绑定优势

与通过 Combine 的 assign(to:on:) 方法订阅相比,通过 CombineUI 的绑定进行订阅更可取,因为它们使用 weak 引用并且可以接受方法参数。

示例

publisher.bind(to: button.bindable.title(for: .normal))

Swift 扩展

许多类型也通过 Swift 扩展增强,提供 Combine 发布者,可以在需要时用作属性包装器的替代方案。

支持的 UI 框架

目前支持 UIKit。 CombineUI 未来可能会支持其他 UI 框架。

最低要求

安装

Swift 包管理器

包依赖

<version> 替换为所需的最低版本。

.package(url: "https://github.com/Tinder/CombineUI.git", from: "<version>")

目标依赖

"CombineUI"

Swift Package Collection

https://swiftpackageindex.cn/Tinder/collection.json

代码示例

所有示例都需要此设置

import Combine
import CombineUI
import UIKit

var cancellables = Set<AnyCancellable>()

属性包装器示例:UIButton

@Button var button = UIButton()

button.setImage(UIImage(systemName: "heart"), for: .normal)

$button
    .sink { print("Tapped") }
    .store(in: &cancellables)

绑定示例:UILabel

let label = UILabel()
let subject = PassthroughSubject<String, Never>()

subject
    .bind(to: label.bindable.text)
    .store(in: &cancellables)

subject.send("Text")

扩展方法示例:UIViewController

let viewController = UIViewController()

let lifecycle = viewController
    .lifecyclePublisher()
    .share()

lifecycle
    .sink { print($0) } // .viewWillAppear | .viewDidAppear | .viewWillDisappear | .viewDidDisappear
    .store(in: &cancellables)

lifecycle
    .isVisiblePublisher()
    .sink { print($0) } // true | false
    .store(in: &cancellables)

综合示例

其他示例可在 此存储库中包含的 Xcode 项目 中找到。 有关指导,请参阅包含设置说明的示例项目 README

速查表

UIKit

属性包装器

属性包装器 投影类型
UIButton @Button AnyPublisher<Void, Never>
UIControl @Control ControlInterface
UIDatePicker @DatePicker DatePickerInterface
UIGestureRecognizer @GestureRecognizer GestureRecognizerInterface
UIPageControl @PageControl AnyPublisher<Int, Never>
UIRefreshControl @RefreshControl AnyPublisher<Void, Never>
UIScrollView @ScrollView ScrollViewInterface
UISearchBar @SearchBar SearchBarInterface
UISegmentedControl @SegmentedControl AnyPublisher<Int, Never>
UISlider @Slider AnyPublisher<Float, Never>
UIStepper @Stepper AnyPublisher<Double, Never>
UISwitch @Switch AnyPublisher<Bool, Never>
UITextField @TextField TextFieldInterface
UITextView @TextView TextViewInterface
UIViewController @ViewController ViewControllerInterface

绑定

绑定 类型
UIActivityIndicatorView style (样式) UIActivityIndicatorView.Style
color (颜色) UIColor
hidesWhenStopped (停止时隐藏) Bool
isAnimating (是否正在动画) Bool
UIButton titleColor(for: UIControl.State) (特定状态下的标题颜色) UIColor
titleShadowColor(for: UIControl.State) (特定状态下的标题阴影颜色) UIColor
title(for: UIControl.State) (特定状态下的标题) String
attributedTitle(for: UIControl.State) (特定状态下的属性化标题) AttributedString
image(for: UIControl.State) (特定状态下的图像) UIImage?
backgroundImage(for: UIControl.State) (特定状态下的背景图像) UIImage?
UIControl isEnabled (是否启用) Bool
UIDatePicker countDownDuration (倒计时持续时间) TimeInterval
date (日期) Date
date(animated: Bool) (设置日期,是否动画) Date
UIGestureRecognizer isEnabled (是否启用) Bool
UIImageView image (图像) UIImage?
highlightedImage (高亮图像) UIImage?
isHighlighted (是否高亮) Bool
UILabel isEnabled (是否启用) Bool
font (字体) UIFont
textColor (文本颜色) UIColor
text (文本) String
attributedText (属性化文本) AttributedString
UIPageControl pageIndicatorTintColor (页面指示器颜色) UIColor
currentPageIndicatorTintColor (当前页面指示器颜色) UIColor
currentPage (当前页面) Int
numberOfPages (页面数量) Int
hidesForSinglePage (单页时隐藏) Bool
UIProgressView trackTintColor (轨道颜色) UIColor
progressTintColor (进度条颜色) UIColor
progress (进度) Float
progress(animated: Bool) (设置进度,是否动画) Float
UIRefreshControl tintColor (着色颜色) UIColor
attributedTitle (属性化标题) AttributedString
isRefreshing (是否正在刷新) Bool
UISegmentedControl isMomentary (是否瞬间) Bool
selectedSegmentIndex (选中的段索引) Int
isEnabledForSegment(at: Int) (指定索引的段是否启用) Bool
widthForSegment(at: Int) (指定索引的段宽度) CGFloat
titleForSegment(at: Int) (指定索引的段标题) String
imageForSegment(at: Int) (指定索引的段图像) UIImage?
UISlider isContinuous (是否连续) Bool
minimumValue (最小值) Float
maximumValue (最大值) Float
minimumTrackTintColor (最小轨道颜色) UIColor
maximumTrackTintColor (最大轨道颜色) UIColor
thumbTintColor (滑块颜色) UIColor
value (值) Float
value(animated: Bool) (设置值,是否动画) Float
UIStepper isContinuous (是否连续) Bool
autorepeat (是否自动重复) Bool
wraps (是否循环) Bool
minimumValue (最小值) Double
maximumValue (最大值) Double
stepValue (步进值) Double
value (值) Double
UISwitch onTintColor (开启颜色) UIColor
thumbTintColor (滑块颜色) UIColor
isOn (是否开启) Bool
isOn(animated: Bool) (设置是否开启,是否动画) Bool
UITextField font (字体) UIFont
textColor (文本颜色) UIColor
textAlignment (文本对齐方式) NSTextAlignment
placeholder (占位符) String
attributedPlaceholder (属性化占位符) AttributedString
text (文本) String
attributedText (属性化文本) AttributedString
UITextView isEditable (是否可编辑) Bool
font (字体) UIFont
textColor (文本颜色) UIColor
textAlignment (文本对齐方式) NSTextAlignment
text (文本) String
attributedText (属性化文本) AttributedString
UIView isUserInteractionEnabled (是否允许用户交互) Bool
isMultipleTouchEnabled (是否允许多点触控) Bool
isExclusiveTouch (是否独占触摸) Bool
clipsToBounds (是否裁剪超出边界的内容) Bool
tintColor (着色颜色) UIColor
backgroundColor (背景颜色) UIColor
borderColor (边框颜色) UIColor
shadowColor (阴影颜色) UIColor
alpha (透明度) CGFloat
isOpaque (是否不透明) Bool
isHidden (是否隐藏) Bool

扩展方法

方法 类型
UIButton tapPublisher() (点击发布者) AnyPublisher<Void, Never>
UIControl publisher(for: UIControl.Event) (指定事件的发布者) AnyPublisher<UIControl.Event, Never>
UIDatePicker countDownDurationPublisher() (倒计时持续时间发布者) AnyPublisher<TimeInterval, Never>
datePublisher() (日期发布者) AnyPublisher<Date, Never>
UIGestureRecognizer publisher(attachingTo: UIView) (附加到 UIView 的发布者) AnyPublisher<UIGestureRecognizer, Never>
UIPageControl currentPagePublisher() (当前页面发布者) AnyPublisher<Int, Never>
UIRefreshControl refreshPublisher() (刷新发布者) AnyPublisher<Void, Never>
UISegmentedControl selectedSegmentIndexPublisher() (选中段索引发布者) AnyPublisher<Int, Never>
UISlider valuePublisher() (值发布者) AnyPublisher<Float, Never>
UIStepper valuePublisher() (值发布者) AnyPublisher<Double, Never>
UISwitch isOnPublisher() (是否开启发布者) AnyPublisher<Bool, Never>
UITextField textPublisher() (文本发布者) AnyPublisher<String, Never>
attributedTextPublisher() (属性化文本发布者) AnyPublisher<AttributedString, Never>
UIViewController lifecyclePublisher() (生命周期事件发布者) AnyPublisher<ViewControllerLifecycleEvent, Never>
Publisher where Output == ViewControllerLifecycleEvent (输出为 ViewControllerLifecycleEvent 的发布者) isVisiblePublisher() (可见性发布者) AnyPublisher<Bool, Never>

文档

UIActivityIndicatorView

绑定

var style: Binding<UIActivityIndicatorView.Style>
var color: Binding<UIColor>
var hidesWhenStopped: Binding<Bool>
var isAnimating: Binding<Bool>

代码示例

let activityIndicatorView = UIActivityIndicatorView()

Just(.medium)
    .bind(to: activityIndicatorView.bindable.style)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: activityIndicatorView.bindable.color)
    .store(in: &cancellables)

Just(true)
    .bind(to: activityIndicatorView.bindable.hidesWhenStopped)
    .store(in: &cancellables)

Just(true)
    .bind(to: activityIndicatorView.bindable.isAnimating)
    .store(in: &cancellables)

UIButton

事件
.primaryActionTriggered (主要操作触发)

属性包装器

@Button // Projected Value: AnyPublisher<Void, Never>

绑定

func titleColor(for state: UIControl.State) -> Binding<UIColor>
func titleShadowColor(for state: UIControl.State) -> Binding<UIColor>
func title(for state: UIControl.State) -> Binding<String>
func attributedTitle(for state: UIControl.State) -> Binding<AttributedString>
func image(for state: UIControl.State) -> Binding<UIImage?>
func backgroundImage(for state: UIControl.State) -> Binding<UIImage?>

扩展方法

func tapPublisher() -> AnyPublisher<Void, Never>

代码示例

// Property Wrapper

@Button var button = UIButton()

button.setImage(UIImage(systemName: "heart"), for: .normal)

$button
    .sink { print("Tapped") }
    .store(in: &cancellables)

// Bindings

Just(.systemPink)
    .bind(to: button.bindable.titleColor(for: .normal))
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: button.bindable.titleShadowColor(for: .normal))
    .store(in: &cancellables)

Just("Title")
    .bind(to: button.bindable.title(for: .normal))
    .store(in: &cancellables)

Just(AttributedString("Title"))
    .bind(to: button.bindable.attributedTitle(for: .normal))
    .store(in: &cancellables)

Just(.checkmark)
    .bind(to: button.bindable.image(for: .normal))
    .store(in: &cancellables)

Just(.checkmark)
    .bind(to: button.bindable.backgroundImage(for: .normal))
    .store(in: &cancellables)

// Extension Method

button
    .tapPublisher()
    .sink { print("Tapped") }
    .store(in: &cancellables)

UIControl

属性包装器

@Control // Projected Value: ControlInterface

ControlInterface

var touchDown: AnyPublisher<Void, Never>
var touchDownRepeat: AnyPublisher<Void, Never>
var touchDragInside: AnyPublisher<Void, Never>
var touchDragOutside: AnyPublisher<Void, Never>
var touchDragEnter: AnyPublisher<Void, Never>
var touchDragExit: AnyPublisher<Void, Never>
var touchUpInside: AnyPublisher<Void, Never>
var touchUpOutside: AnyPublisher<Void, Never>
var touchCancel: AnyPublisher<Void, Never>
var valueChanged: AnyPublisher<Void, Never>
var menuActionTriggered: AnyPublisher<Void, Never>
var primaryActionTriggered: AnyPublisher<Void, Never>
var editingDidBegin: AnyPublisher<Void, Never>
var editingChanged: AnyPublisher<Void, Never>
var editingDidEnd: AnyPublisher<Void, Never>
var editingDidEndOnExit: AnyPublisher<Void, Never>

绑定

var isEnabled: Binding<Bool>

扩展方法

func publisher(for controlEvents: UIControl.Event) -> AnyPublisher<UIControl.Event, Never>

在接收到的 OptionSet 上使用 contains() 来确定发生了哪个事件。

支持的类型

正如每个 CombineUI 属性包装器和绑定可以与其支持类型的子类一起使用一样,UIControl 属性包装器和绑定与 UIControl 子类兼容,包括(但不限于)以下内容

代码示例

// Property Wrapper

@Control var control = UIButton()

control.setImage(UIImage(systemName: "heart"), for: .normal)

$control
    .primaryActionTriggered
    .sink { print("Triggered") }
    .store(in: &cancellables)

// Binding

Just(true)
    .bind(to: control.bindable.isEnabled)
    .store(in: &cancellables)

// Extension Method

control
    .publisher(for: .primaryActionTriggered)
    .sink { controlEvents in }
    .store(in: &cancellables)

注释

UIDatePicker

属性 事件
.countDownDuration (倒计时持续时间) .valueChanged (值改变)
.date (日期) .valueChanged (值改变)

属性包装器

@DatePicker(mode: UIDatePicker.Mode = .dateAndTime) // Projected Value: DatePickerInterface

DatePickerInterface

var countDownDuration: AnyPublisher<TimeInterval, Never>
var date: AnyPublisher<Date, Never>

绑定

var countDownDuration: Binding<TimeInterval>
var date: Binding<Date>

func date(animated: Bool) -> Binding<Date>

扩展方法

func datePublisher() -> AnyPublisher<Date, Never>
func countDownDurationPublisher() -> AnyPublisher<TimeInterval, Never>

代码示例

// Property Wrapper

@DatePicker(mode: .countDownTimer)
var datePicker1 = UIDatePicker()

@DatePicker var datePicker2 = UIDatePicker()

$datePicker1
    .countDownDuration
    .sink { countDownDuration in }
    .store(in: &cancellables)

$datePicker2
    .date
    .sink { date in }
    .store(in: &cancellables)

// Bindings

Just(60)
    .bind(to: datePicker1.bindable.countDownDuration)
    .store(in: &cancellables)

Just(Date())
    .bind(to: datePicker2.bindable.date)
    .store(in: &cancellables)

Just(Date())
    .bind(to: datePicker2.bindable.date(animated: true))
    .store(in: &cancellables)

// Extension Methods

datePicker1
    .countDownDurationPublisher()
    .sink { countDownDuration in }
    .store(in: &cancellables)

datePicker2
    .datePublisher()
    .sink { date in }
    .store(in: &cancellables)

UIGestureRecognizer

属性包装器

@GestureRecognizer // Projected Value: GestureRecognizerInterface

GestureRecognizerInterface

func attaching(to view: UIView) -> AnyPublisher<UIGestureRecognizer, Never>

绑定

var isEnabled: Binding<Bool>

扩展方法

func publisher(attachingTo view: UIView) -> AnyPublisher<UIGestureRecognizer, Never>

代码示例

// Property Wrapper

@GestureRecognizer var swipe = UISwipeGestureRecognizer()

$swipe
    .attaching(to: view)
    .sink { swipe in }
    .store(in: &cancellables)

// Binding

Just(true)
    .bind(to: swipe.bindable.isEnabled)
    .store(in: &cancellables)

// Extension Method

swipe
    .publisher(attachingTo: view)
    .sink { swipe in }
    .store(in: &cancellables)

注释

UIImageView

绑定

var image: Binding<UIImage?>
var highlightedImage: Binding<UIImage?>
var isHighlighted: Binding<Bool>

代码示例

let imageView = UIImageView()

Just(.checkmark)
    .bind(to: imageView.bindable.image)
    .store(in: &cancellables)

Just(.checkmark)
    .bind(to: imageView.bindable.highlightedImage)
    .store(in: &cancellables)

Just(true)
    .bind(to: imageView.bindable.isHighlighted)
    .store(in: &cancellables)

UILabel

绑定

var isEnabled: Binding<Bool>
var font: Binding<UIFont>
var textColor: Binding<UIColor>
var text: Binding<String>
var attributedText: Binding<AttributedString>

代码示例

let label = UILabel()

Just(true)
    .bind(to: label.bindable.isEnabled)
    .store(in: &cancellables)

Just(.preferredFont(forTextStyle: .body))
    .bind(to: label.bindable.font)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: label.bindable.textColor)
    .store(in: &cancellables)

Just("Text")
    .bind(to: label.bindable.text)
    .store(in: &cancellables)

Just(AttributedString("Text"))
    .bind(to: label.bindable.attributedText)
    .store(in: &cancellables)

UIPageControl

属性 事件
.currentPage (当前页面) .valueChanged (值改变)

属性包装器

@PageControl // Projected Value: AnyPublisher<Int, Never>

绑定

var pageIndicatorTintColor: Binding<UIColor>
var currentPageIndicatorTintColor: Binding<UIColor>
var currentPage: Binding<Int>
var numberOfPages: Binding<Int>
var hidesForSinglePage: Binding<Bool>

扩展方法

func currentPagePublisher() -> AnyPublisher<Int, Never>

代码示例

// Property Wrapper

@PageControl var pageControl = UIPageControl()

$pageControl
    .sink { currentPage in }
    .store(in: &cancellables)

// Bindings

Just(.systemPink)
    .bind(to: pageControl.bindable.pageIndicatorTintColor)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: pageControl.bindable.currentPageIndicatorTintColor)
    .store(in: &cancellables)

Just(1)
    .bind(to: pageControl.bindable.currentPage)
    .store(in: &cancellables)

Just(1)
    .bind(to: pageControl.bindable.numberOfPages)
    .store(in: &cancellables)

Just(true)
    .bind(to: pageControl.bindable.hidesForSinglePage)
    .store(in: &cancellables)

// Extension Method

pageControl
    .currentPagePublisher()
    .sink { currentPage in }
    .store(in: &cancellables)

UIProgressView

绑定

var trackTintColor: Binding<UIColor>
var progressTintColor: Binding<UIColor>
var progress: Binding<Float>

func progress(animated: Bool) -> Binding<Float>

代码示例

let progressView = UIProgressView()

Just(.systemPink)
    .bind(to: progressView.bindable.trackTintColor)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: progressView.bindable.progressTintColor)
    .store(in: &cancellables)

Just(1)
    .bind(to: progressView.bindable.progress)
    .store(in: &cancellables)

Just(1)
    .bind(to: progressView.bindable.progress(animated: true))
    .store(in: &cancellables)

UIRefreshControl

属性 事件
.isRefreshing (是否正在刷新) .valueChanged (值改变) true (真)

属性包装器

@RefreshControl // Projected Value: AnyPublisher<Void, Never>

绑定

var tintColor: Binding<UIColor>
var attributedTitle: Binding<AttributedString>
var isRefreshing: Binding<Bool>

扩展方法

func refreshPublisher() -> AnyPublisher<Void, Never>

代码示例

// Property Wrapper

@RefreshControl var refreshControl = UIRefreshControl()

$refreshControl
    .sink { print("Refreshing") }
    .store(in: &cancellables)

// Bindings

Just(.systemPink)
    .bind(to: refreshControl.bindable.tintColor)
    .store(in: &cancellables)

Just(AttributedString("Title"))
    .bind(to: refreshControl.bindable.attributedTitle)
    .store(in: &cancellables)

Just(true)
    .bind(to: refreshControl.bindable.isRefreshing)
    .store(in: &cancellables)

// Extension Method

refreshControl
    .refreshPublisher()
    .sink { print("Refreshing") }
    .store(in: &cancellables)

UIScrollView

协议
UIScrollViewDelegate

属性包装器

@ScrollView // Projected Value: ScrollViewInterface

ScrollViewInterface

var didScroll: AnyPublisher<Void, Never>
var didZoom: AnyPublisher<Void, Never>
var willBeginDragging: AnyPublisher<Void, Never>
var willEndDragging: AnyPublisher<ScrollViewWillEndDragging, Never>
var didEndDragging: AnyPublisher<Bool, Never>
var willBeginDecelerating: AnyPublisher<Void, Never>
var didEndDecelerating: AnyPublisher<Void, Never>
var didEndScrollingAnimation: AnyPublisher<Void, Never>
var willBeginZooming: AnyPublisher<UIView?, Never>
var didEndZooming: AnyPublisher<ScrollViewDidEndZooming, Never>
var didScrollToTop: AnyPublisher<Void, Never>
var didChangeAdjustedContentInset: AnyPublisher<Void, Never>

代码示例

@ScrollView var scrollView = UIScrollView()

$scrollView
    .didScroll
    .sink { print("Scrolling") }
    .store(in: &cancellables)

UISearchBar

协议
UISearchBarDelegate

属性包装器

@SearchBar // Projected Value: SearchBarInterface

SearchBarInterface

var textDidBeginEditing: AnyPublisher<Void, Never>
var textDidEndEditing: AnyPublisher<Void, Never>
var textDidChange: AnyPublisher<String, Never>
var searchButtonClicked: AnyPublisher<Void, Never>
var bookmarkButtonClicked: AnyPublisher<Void, Never>
var cancelButtonClicked: AnyPublisher<Void, Never>
var resultsListButtonClicked: AnyPublisher<Void, Never>
var selectedScopeButtonIndexDidChange: AnyPublisher<Int, Never>

代码示例

@SearchBar var searchBar = UISearchBar()

$searchBar
    .textDidChange
    .sink { text in }
    .store(in: &cancellables)

UISegmentedControl

属性 事件
.selectedSegmentIndex (选中段索引) .valueChanged (值改变)

属性包装器

@SegmentedControl // Projected Value: AnyPublisher<Int, Never>

绑定

var isMomentary: Binding<Bool>
var selectedSegmentIndex: Binding<Int>

func isEnabledForSegment(at index: Int) -> Binding<Bool>
func widthForSegment(at index: Int) -> Binding<CGFloat>
func titleForSegment(at index: Int) -> Binding<String>
func imageForSegment(at index: Int) -> Binding<UIImage?>

扩展方法

func selectedSegmentIndexPublisher() -> AnyPublisher<Int, Never>

代码示例

// Property Wrapper

@SegmentedControl var segmentedControl = UISegmentedControl(items: items)

$segmentedControl
    .sink { selectedSegmentIndex in }
    .store(in: &cancellables)

// Bindings

Just(true)
    .bind(to: segmentedControl.bindable.isMomentary)
    .store(in: &cancellables)

Just(1)
    .bind(to: segmentedControl.bindable.selectedSegmentIndex)
    .store(in: &cancellables)

Just(true)
    .bind(to: segmentedControl.bindable.isEnabledForSegment(at: 1))
    .store(in: &cancellables)

Just(100)
    .bind(to: segmentedControl.bindable.widthForSegment(at: 1))
    .store(in: &cancellables)

Just("Title")
    .bind(to: segmentedControl.bindable.titleForSegment(at: 1))
    .store(in: &cancellables)

Just(.checkmark)
    .bind(to: segmentedControl.bindable.imageForSegment(at: 1))
    .store(in: &cancellables)

// Extension Method

segmentedControl
    .selectedSegmentIndexPublisher()
    .sink { selectedSegmentIndex in }
    .store(in: &cancellables)

UISlider

属性 事件
.value (值) .valueChanged (值改变)

属性包装器

@Slider // Projected Value: AnyPublisher<Float, Never>

绑定

var isContinuous: Binding<Bool>
var minimumValue: Binding<Float>
var maximumValue: Binding<Float>
var minimumTrackTintColor: Binding<UIColor>
var maximumTrackTintColor: Binding<UIColor>
var thumbTintColor: Binding<UIColor>
var value: Binding<Float>

func value(animated: Bool) -> Binding<Float>

扩展方法

func valuePublisher() -> AnyPublisher<Float, Never>

代码示例

// Property Wrapper

@Slider var slider = UISlider()

$slider
    .sink { value in }
    .store(in: &cancellables)

// Bindings

Just(true)
    .bind(to: slider.bindable.isContinuous)
    .store(in: &cancellables)

Just(1)
    .bind(to: slider.bindable.minimumValue)
    .store(in: &cancellables)

Just(100)
    .bind(to: slider.bindable.maximumValue)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: slider.bindable.minimumTrackTintColor)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: slider.bindable.maximumTrackTintColor)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: slider.bindable.thumbTintColor)
    .store(in: &cancellables)

Just(1)
    .bind(to: slider.bindable.value)
    .store(in: &cancellables)

Just(1)
    .bind(to: slider.bindable.value(animated: true))
    .store(in: &cancellables)

// Extension Method

slider
    .valuePublisher()
    .sink { value in }
    .store(in: &cancellables)

UIStepper

属性 事件
.value (值) .valueChanged (值改变)

属性包装器

@Stepper // Projected Value: AnyPublisher<Double, Never>

绑定

var isContinuous: Binding<Bool>
var autorepeat: Binding<Bool>
var wraps: Binding<Bool>
var minimumValue: Binding<Double>
var maximumValue: Binding<Double>
var stepValue: Binding<Double>
var value: Binding<Double>

扩展方法

func valuePublisher() -> AnyPublisher<Double, Never>

代码示例

// Property Wrapper

@Stepper var stepper = UIStepper()

$stepper
    .sink { value in }
    .store(in: &cancellables)

// Bindings

Just(true)
    .bind(to: stepper.bindable.isContinuous)
    .store(in: &cancellables)

Just(true)
    .bind(to: stepper.bindable.autorepeat)
    .store(in: &cancellables)

Just(true)
    .bind(to: stepper.bindable.wraps)
    .store(in: &cancellables)

Just(1)
    .bind(to: stepper.bindable.minimumValue)
    .store(in: &cancellables)

Just(100)
    .bind(to: stepper.bindable.maximumValue)
    .store(in: &cancellables)

Just(10)
    .bind(to: stepper.bindable.stepValue)
    .store(in: &cancellables)

Just(100)
    .bind(to: stepper.bindable.value)
    .store(in: &cancellables)

// Extension Method

stepper
    .valuePublisher()
    .sink { value in }
    .store(in: &cancellables)

UISwitch

属性 事件
.isOn (是否开启) .valueChanged (值改变)

属性包装器

@Switch // Projected Value: AnyPublisher<Bool, Never>

绑定

var onTintColor: Binding<UIColor>
var thumbTintColor: Binding<UIColor>
var isOn: Binding<Bool>

func isOn(animated: Bool) -> Binding<Bool>

扩展方法

func isOnPublisher() -> AnyPublisher<Bool, Never>

代码示例

// Property Wrapper

@Switch var `switch` = UISwitch()

$switch
    .sink { isOn in }
    .store(in: &cancellables)

// Bindings

Just(.systemPink)
    .bind(to: `switch`.bindable.onTintColor)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: `switch`.bindable.thumbTintColor)
    .store(in: &cancellables)

Just(true)
    .bind(to: `switch`.bindable.isOn)
    .store(in: &cancellables)

Just(true)
    .bind(to: `switch`.bindable.isOn(animated: true))
    .store(in: &cancellables)

// Extension Method

`switch`
    .isOnPublisher()
    .sink { isOn in }
    .store(in: &cancellables)

UITextField

属性 事件
.text (文本) .allEditingEvents (所有编辑事件)
.attributedText (属性化文本) .allEditingEvents (所有编辑事件)
协议
UITextFieldDelegate

属性包装器

@TextField // Projected Value: TextFieldInterface

TextFieldInterface

// Properties

var text: AnyPublisher<String, Never>
var attributedText: AnyPublisher<AttributedString, Never>

// UITextFieldDelegate

var didBeginEditing: AnyPublisher<Void, Never>
var didEndEditing: AnyPublisher<Void, Never>
var didChangeSelection: AnyPublisher<Void, Never>

绑定

var font: Binding<UIFont>
var textColor: Binding<UIColor>
var textAlignment: Binding<NSTextAlignment>
var placeholder: Binding<String>
var attributedPlaceholder: Binding<AttributedString>
var text: Binding<String>
var attributedText: Binding<AttributedString>

扩展方法

func textPublisher() -> AnyPublisher<String, Never>
func attributedTextPublisher() -> AnyPublisher<AttributedString, Never>

代码示例

// Property Wrapper

@TextField var textField = UITextField()

$textField
    .text
    .sink { text in }
    .store(in: &cancellables)

$textField
    .attributedText
    .sink { attributedText in }
    .store(in: &cancellables)

// Bindings

Just(.preferredFont(forTextStyle: .body))
    .bind(to: textField.bindable.font)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: textField.bindable.textColor)
    .store(in: &cancellables)

Just(.natural)
    .bind(to: textField.bindable.textAlignment)
    .store(in: &cancellables)

Just("Placeholder")
    .bind(to: textField.bindable.placeholder)
    .store(in: &cancellables)

Just(AttributedString("Placeholder"))
    .bind(to: textField.bindable.attributedPlaceholder)
    .store(in: &cancellables)

Just("Text")
    .bind(to: textField.bindable.text)
    .store(in: &cancellables)

Just(AttributedString("Text"))
    .bind(to: textField.bindable.attributedText)
    .store(in: &cancellables)

// Extension Methods

textField
    .textPublisher()
    .sink { text in }
    .store(in: &cancellables)

textField
    .attributedTextPublisher()
    .sink { attributedText in }
    .store(in: &cancellables)

UITextView

协议
UITextViewDelegate

属性包装器

@TextView // Projected Value: TextViewInterface

TextViewInterface

// Properties

var text: AnyPublisher<String, Never>
var attributedText: AnyPublisher<AttributedString, Never>

// UITextViewDelegate

var didChange: AnyPublisher<Void, Never>
var didBeginEditing: AnyPublisher<Void, Never>
var didEndEditing: AnyPublisher<Void, Never>
var didChangeSelection: AnyPublisher<Void, Never>

绑定

var isEditable: Binding<Bool>
var font: Binding<UIFont>
var textColor: Binding<UIColor>
var textAlignment: Binding<NSTextAlignment>
var text: Binding<String>
var attributedText: Binding<AttributedString>

代码示例

// Property Wrapper

@TextView var textView = UITextView()

$textView
    .text
    .sink { text in }
    .store(in: &cancellables)

$textView
    .attributedText
    .sink { attributedText in }
    .store(in: &cancellables)

// Bindings

Just(true)
    .bind(to: textView.bindable.isEditable)
    .store(in: &cancellables)

Just(.preferredFont(forTextStyle: .body))
    .bind(to: textView.bindable.font)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: textView.bindable.textColor)
    .store(in: &cancellables)

Just(.natural)
    .bind(to: textView.bindable.textAlignment)
    .store(in: &cancellables)

Just("Text")
    .bind(to: textView.bindable.text)
    .store(in: &cancellables)

Just(AttributedString("Text"))
    .bind(to: textView.bindable.attributedText)
    .store(in: &cancellables)

UIView

绑定

var isUserInteractionEnabled: Binding<Bool>
var isMultipleTouchEnabled: Binding<Bool>
var isExclusiveTouch: Binding<Bool>
var clipsToBounds: Binding<Bool>
var tintColor: Binding<UIColor>
var backgroundColor: Binding<UIColor>
var borderColor: Binding<UIColor>
var shadowColor: Binding<UIColor>
var alpha: Binding<CGFloat>
var isOpaque: Binding<Bool>
var isHidden: Binding<Bool>

支持的类型

正如每个 CombineUI 绑定可以与其支持类型的子类一起使用一样,UIView 绑定与所有 UIView 子类兼容。

代码示例

let view = UIView()

Just(true)
    .bind(to: view.bindable.isUserInteractionEnabled)
    .store(in: &cancellables)

Just(true)
    .bind(to: view.bindable.isMultipleTouchEnabled)
    .store(in: &cancellables)

Just(true)
    .bind(to: view.bindable.isExclusiveTouch)
    .store(in: &cancellables)

Just(true)
    .bind(to: view.bindable.clipsToBounds)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: view.bindable.tintColor)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: view.bindable.backgroundColor)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: view.bindable.borderColor)
    .store(in: &cancellables)

Just(.systemPink)
    .bind(to: view.bindable.shadowColor)
    .store(in: &cancellables)

Just(0.5)
    .bind(to: view.bindable.alpha)
    .store(in: &cancellables)

Just(true)
    .bind(to: view.bindable.isOpaque)
    .store(in: &cancellables)

Just(true)
    .bind(to: view.bindable.isHidden)
    .store(in: &cancellables)

UIViewController

属性包装器

@ViewController // Projected Value: ViewControllerInterface

ViewControllerInterface

var isVisible: AnyPublisher<Bool, Never>
var viewWillAppear: AnyPublisher<Void, Never>
var viewDidAppear: AnyPublisher<Void, Never>
var viewWillDisappear: AnyPublisher<Void, Never>
var viewDidDisappear: AnyPublisher<Void, Never>

扩展方法

func lifecyclePublisher() -> AnyPublisher<ViewControllerLifecycleEvent, Never>

发布者扩展

func isVisiblePublisher() -> AnyPublisher<Bool, Failure> where Output == ViewControllerLifecycleEvent

代码示例

// Property Wrapper

@ViewController var viewController = UIViewController()

$viewController
    .isVisible
    .sink { isVisible in }
    .store(in: &cancellables)

$viewController
    .viewWillAppear
    .sink { print("Appearing") }
    .store(in: &cancellables)

$viewController
    .viewDidAppear
    .sink { print("Appeared") }
    .store(in: &cancellables)

$viewController
    .viewWillDisappear
    .sink { print("Disappearing") }
    .store(in: &cancellables)

$viewController
    .viewDidDisappear
    .sink { print("Disappeared") }
    .store(in: &cancellables)

// Extension Methods

let lifecycle = viewController
    .lifecyclePublisher()
    .share()

lifecycle
    .sink { event in }
    .store(in: &cancellables)

lifecycle
    .isVisiblePublisher()
    .sink { isVisible in }
    .store(in: &cancellables)

注释

override func viewDidLoad() {
    super.viewDidLoad()
    lifecyclePublisher()
        .isVisiblePublisher()
        .sink { isVisible in }
        .store(in: &cancellables)
}

注意事项

委托

CombineUI 为常见的委托协议方法提供发布者,但是由于发布者的性质,具有返回值的委托方法不可用作发布者。 此外,CombineUI 提供的委托方法发布者仅作为一种便利方式提供。 对于复杂的设置,甚至当需要超过少数几个委托方法时,建议使用实际的委托类实例。

另请注意,设置委托属性将禁用委托发布者。 这意味着不可能将实际的委托类实例与委托发布者一起使用。 因此,针对每个特定用例选择一种模式或另一种模式。

自定义

将绑定添加到视图和控件

可以轻松地将其他绑定添加到现有视图和控件。 这对于 CombineUI 尚未原生支持的属性很有用。

示例

extension Bindable where Target: UIView {

    var tag: Binding<Int> {
        Binding(self, for: \.tag)
    }
}

在自定义视图和控件中采用 CombineUI

自定义视图和控件也可以采用 CombineUI 提供的相同类型的 API。

示例

@propertyWrapper
struct Example<T: ExampleControl> {

    var wrappedValue: T
    var projectedValue: AnyPublisher<ExampleValue, Never>

    init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
        self.projectedValue = wrappedValue
            .valuePublisher()
            .share()
            .eraseToAnyPublisher()
    }
}

extension Bindable where Target: ExampleControl {

    var value: Binding<ExampleValue> {
        Binding(self, for: \.value)
    }
}

extension ExampleControl {

    func valuePublisher() -> AnyPublisher<ExampleValue, Never> {
        publisher(for: .valueChanged)
            .compactMap { [weak self] _ in self?.value }
            .prepend(value)
            .eraseToAnyPublisher()
    }
}

CombineUI 源代码可用作其他示例的参考。

贡献

虽然非常感谢您对贡献该项目的兴趣,但它仅为了与社区分享的目的而开源。 这意味着我们目前无法接受外部贡献,并且不会审查或合并拉取请求。 要报告安全问题或漏洞,请提交 GitHub 问题。

许可

根据 Match Group Modified 3-Clause BSD 许可获得许可。