WandioCoreComponents
一组自定义 UI 组件。
在内容和阴影周围都创建一个圆形半径的视图
- RoundedShadowedView
- RoundedShadowedButton
- RoundedShadowedControl
- RoundedShadowedTextField
示例
要运行示例项目,请克隆仓库并构建 WandioCoreComponentsExample 目标。
安装
WandioCoreComponents 通过 CocoaPods 提供使用。要安装它,只需将以下行添加到 Podfile 中
pod 'WandioCoreComponents'
用法
首先导入 WandioCoreComponents
import WandioCoreComponents
RoundedShadowedView
let shadowedView = RoundedShadowedView(frame: CGRect(x: 40, y: 40, width: 200, height: 100))
shadowedView.backgroundLayerColor = .red
shadowedView.backgroundLayerLineWidth = 4
shadowedView.backgroundLayerStrokeColor = .yellow
shadowedView.shadowColor = .black
shadowedView.shadowAlpha = 0.7
shadowedView.shadowRadius = 24
shadowedView.cornerRadius = 20
shadowedView.shadowOffset = CGSize(width: 3, height: 8)
view.addSubview(shadowedView)
或者设置以来Storyboard中的值
您可以将这些值乘以screenFactor
以适应所有设备。如果是这样,您需要先设置screenFactor
。例如,当应用完成启动时。默认的 screenFactor 值为 1.0。
- RoundedShadowedButton, RoundedShadowedControl, RoundedShadowedTextField
使用与RoundedShadowedView
相同的步骤。
CustomIntensityVisualEffectView
具有自定义强度值的视觉效果视图。您可以将其初始化为其父 UIVisualEffectView。唯一的不同之处在于您可以为效果提供强度值。
OTPView
您可以通过提供所需的文本字段数量来初始化单次有效密码视图,并处理所有文本字段之间的切换、自动填充、粘贴、返回当前 OTP 字符串、通知是否填写了所有字段等。
您可以子类化 OTPTextField
并注册您的自定义字段
class CustomField: OTPTextField {
// Create your custom textfield
}
class ViewController: UIViewController {
let otpView = OTPView()
override func viewDidLoad() {
super.viewDidLoad()
otpView.register(CustomField.self)
}
}
OTPTextField
是 RoundedShadowedTextField
的子类,因此您可以给它提供阴影和圆角半径。
您可以为文本字段提供水平、垂直填充和彼此之间的间距
otpView.spacing = 8
otpView.verticalPadding = 12
otpView.horizontalPadding = 20
verticalPadding
会被除以二,结果分配给字段的 y
原点。同样,horizontalPadding
也会被除以二,但其结果分配给第一个字段的 x
原点。
您可以为 OTPView
设置 delegate
并实现方法。当设置委托或显式调用 OTPView
上的 reload()
时,会调用这些方法。
func otpView(_ view: OTPView, didChangeValidity isValid: Bool, otp: String) // is valid if all fields are filled
func otpView(_ view: OTPView, textField field: OTPTextField, at index: Int) // returns the field at corresponding index and you can make some special modifications there.
通过显式调用 remakeFields()
,将删除所有当前字段,并使用提供的字段数量创建新的字段。
您可以通过调用 getOTP()
来获取当前的 OTP 字符串,通过调用 showKeyboard()
或 hideKeyboard()
(显示键盘并聚焦到第一个字段)来显示/隐藏键盘,并通过调用 reset()
来重置 OTP 字符串。
通过在 OTPView
上调用 func updateState(_ state: OTPTextFieldState)
,您可以触发每个字段的 updateState
实现,并提供 .normal
或 .error
状态。
OTPTextField
OTPTextField
对 previousTextField
和 nextTextField
有 weak
引用。
它还具有状态 OTPTextFieldState
,包含了 .normal
和 .error
的情况。您可以通过调用 updateState(.error)
等方式改变状态,默认情况下,它将设置笔触颜色为 .red
并将边框的线条宽度设置为 2
。当然,您可以通过子类化 OTPTextField
并重写 func updateState(_ state: OTPTextFieldState)
方法来创建自己的 UI 逻辑。
WandioBottomSheet
可扩展/折叠的底部框主要由三个主要内容构成:背景视图、处理区域和内容视图。您可以拖动处理区域或内容视图以展开、折叠或关闭底部框。点击背景也会触发关闭。
您可以通过继承 WandioBottomSheet
来创建自己的处理和内容实例。您可以使用 WandioBottomSheetHandlerView
作为处理区域或创建自己的自定义视图。处理区域可以通过不提供它来忽略,并且您可以用仅内容视图来使用。
class BottomSheetContent: UIView {
let child = UIView()
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(child)
child.backgroundColor = .clear
child.translatesAutoresizingMaskIntoConstraints = false
child.heightAnchor.constraint(equalToConstant: 360).isActive = true
child.topAnchor.constraint(equalTo: topAnchor).isActive = true
child.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
child.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
child.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
label.text = "Content"
label.translatesAutoresizingMaskIntoConstraints = false
child.addSubview(label)
label.centerXAnchor.constraint(equalTo: child.centerXAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: child.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError()
}
}
class CustomBottomSheet: WandioBottomSheet {
private let handler = WandioBottomSheetHandlerView()
private let content = BottomSheetContent()
public override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func configure() {
contentHeight = 300 * screenFactor // setting contentHeight will fix content height by given value and ignore its original height
addHandle(handler)
addContent(content)
}
}
class YourViewController: UIViewController {
// Present bottom sheet by calling present on view controller or view itself
@objc private func presentDefaultWandioBottomSheet() {
DefaultWandioBottomSheet().present(on: self)
}
@objc private func presentCustomBottomSheet() {
CustomBottomSheet().present(on: self)
}
}
通过设置底部框的 delegate
,您可以在拖动过程中实现这些方法并添加自己的自定义逻辑,例如在视图控制器或其他地方添加一些额外的动画。
/// Tells the delegate that bottom sheet pan gesture began
func bottomSheet(_ sheet: WandioBottomSheet, didBeginPanGesture recognizer: UIPanGestureRecognizer)
/// Tells the delegate that bottom sheet pan gesture changed
func bottomSheet(_ sheet: WandioBottomSheet, didChangePanGesture recognizer: UIPanGestureRecognizer)
/// Tells the delegate that bottom sheet pan gesture ended
func bottomSheet(_ sheet: WandioBottomSheet, didEndPanGesture recognizer: UIPanGestureRecognizer)
您可以在 WandioBottomSheet
子类中覆盖拖动手势期间调用的方法来自定义行为。
open func beganPanGesture(_ recognizer: UIPanGestureRecognizer)
open func changedPanGesture(_ recognizer: UIPanGestureRecognizer)
open func endedPanGesture(_ recognizer: UIPanGestureRecognizer)
您可以通过调用来添加内容和处理区域的子视图。
func addContent(_ content: UIView, at index: Int? = nil)
func addHandle(_ handle: UIView, at index: Int? = nil)
ImageLoader
从网络下载图像或从缓存中加载它。通过对 UIImageView
的简单扩展,您可以选择 UIImage
并设置它。
let imageView = UIImageView()
let url = URL(string: "www.yourimageurl.com")!
imageView.setImage(from: url, placeholderImage: UIImage(named: "youtImage"), cachePolicy: .useProtocolCachePolicy, completion: nil)
LoaderView
UIView
子类,具有 isLoading
属性和 start()
及 stop()
方法来设置 isLoading
为真或假。继承 LoaderView
并创建自己的自定义加载器。重写 start
和 stop
方法并触发你自己的自定义动画等。一旦你的自定义加载器完成,你可以将其分配到 LoaderView.shared
,并通过简单地调用 startLoader()
和 stopLoader()
(要么在视图控制器上,要么在视图上)来开始或停止你的自定义加载动画。或者,你可以有你的自定义加载器实例在你的控制器或视图中,并将其作为参数发送。例如,startLoader(YourCustomLoader())
假设你有一个自定义加载器类
class MyLoader: LoaderView {
let indicator = UIActivityIndicatorView(style: .large)
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
backgroundColor = UIColor.red.withAlphaComponent(0.5)
addSubview(indicator)
indicator.translatesAutoresizingMaskIntoConstraints = false
indicator.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
indicator.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
}
override func start() {
super.start()
indicator.startAnimating()
}
override func stop() {
super.stop()
indicator.stopAnimating()
}
}
将加载器设置为 LoaderView.shared
实例,并且调用不带参数的加载方法将呈现出你的自定义加载器
class ViewController: UIViewController {
let loader = MyLoader()
override func viewDidLoad() {
super.viewDidLoad()
LoaderView.shared = loader
}
@IBAction func buttonTap(_ sender: UIButton) {
startLoader()
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
self.stopLoader()
}
}
}
或者,如果你想为特殊情况进行不同的加载器,你可以将加载器实例存储在你的类中,并将其作为参数发送
class ViewController: UIViewController {
let loader = MyLoader()
override func viewDidLoad() {
super.viewDidLoad()
LoaderView.shared = loader
}
@IBAction func buttonTap(_ sender: UIButton) {
startLoader(loader)
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
self.stopLoader(loader)
}
}
}
作者
卡奇·基克纳兹,[email protected]
许可证
WandioCoreComponents 在MIT 许可证下可用。有关更多信息,请参阅Licence文件。