iOS上的IBDesignable自定义垂直滑块

大家好,

前段时间,我们的设计师想要一个带有垂直滑块的批准页面。 此页面位于某些表单屏幕之后(用户在其中填写一些信息),并且通过向下滑动滑块可以看到其下面的页面。 同样,通过将滑块滑到底部,用户可以批准他填写的信息。

您可以在顶部看到一个丑陋的演示。 (完全由我设计designed)

对于此任务,我选择创建IBDesignable自定义滑块,该滑块使用xib文件通过情节提要板在其他项目中重复使用它。 我的主要目的是能够在故事板上看到我的自定义滑块。 我希望获得有关自定义视图的即时视觉反馈,而不是编译和运行项目。

通过使用IBDesignable您可以从情节IBDesignable中实时编辑通过xib创建的自定义视图,我想这真是太酷了。

在这篇文章中,我们有2章:

  • 第1章:如何创建使用xib的自定义垂直滑块
  • 第2章:如何制作 包含 IBInspectable 元素 IBDesignable 自定义视图

请注意,本文中将包含大量代码,我不想撰写单独的文章以保持完整性。 因此,您的职位很长。 希望您能找到对您有用的东西。

  1. 创建XIB
  2. 通过协议功能实现平移手势动作和与父视图的通讯
  3. 实现视图的实现方法

这些是我们要采取的步骤。 让我们开始使用滑块:

1.创建XIB

在此步骤中,我添加了一些屏幕截图,这些屏幕截图将对您有所帮助。 您也可以在Github上查看该项目以亲自查看。 您可以在文章末尾找到链接。

您可以在屏幕快照中查看视图层次结构及其IBOutlets

  • SliderPath:所有项目均发生的位置。 平移手势使用此视图获取坐标反馈。
  • thumbView:他认为您用手指滑动。 这也连接到手势识别器。
  • destinationView:隐藏视图以检查拇指是否已到达目标。 您可以在底部选择的第一个屏幕截图中看到此视图。
  • 我选择在xib上添加平移手势,并连接其动作和插座,如相关屏幕截图所示。

2.执行平移手势

相关的IBOutlets和变量

  var委托:KKVerticalSliderDelegate? 
@IBOutlet弱fileprivate var slidePath:UIView!
@IBOutlet弱fileprivate var拇指:UIView!
@IBOutlet弱的fileprivate var目标:UIView!
@IBOutlet弱fileprivate var thumbTopConstraint:NSLayoutConstraint!
@IBOutlet fileprivate var panGesture:UIPanGestureRecognizer!

与父视图通信的协议。 您可以从它们的名称中了解这些方法的作用。

 协议KKVerticalSliderDelegate { 
func thumbReachedDestination()
func thumbCurrentPosition(_ position:CGFloat)
}

此计算的属性检查拇指是否已到达滑块的底部。

  var isDestinationReached:Bool { 
得到{
let distanceFromDestination:CGFloat = self.destination.center.y-self.thumb.center.y
返回distanceFromDestination <1
}
}

计算拇指的平移。 然后更改拇指的顶部约束以设置其新位置。

获得翻译后,我们将考虑上下边界以及拇指大小为原始翻译设置新的值。

因为约束随着AutoLayout不断变化,所以UIView.animate()以很小的持续时间调用layoutIfNeeded()以提供平滑的滑动效果。

  func slideThumb(){让minY = CGFloat(0) 
让maxY = sliderPath.bounds.size.height-thumb.bounds.size.height var translation = panGesture.translation(in:sliderPath)
var draggedDistance = thumbTopConstraint.constant + translation.y // //防止从路径边界向上移动
如果draggedDistance <0 {
draggedDistance = minY
translation.y + = thumbTopConstraint.constant-minY
}
//防止从路径范围下降
否则,如果draggedDistance> maxY {
draggedDistance = maxY
translation.y + = thumbTopConstraint.constant-maxY
}
其他{
translation.y = 0
} self.thumbTopConstraint.constant = draggedDistance
self.panGesture.setTranslation(translation,in:sliderPath)UIView.animate(withDuration:0.05,delay:0,options:.beginFromCurrentState,animations:{
self.layoutIfNeeded()
},完成:无)
}

通过更改其顶部约束将拇指返回到初始位置。

  func returnInitialLocationAnimated(_动画:布尔){ 
thumbTopConstraint.constant = initialTopConstraint
如果(动画){
UIView.animate(withDuration:0.3,delay:0,options:.beginFromCurrentState,animations:{
self.layoutIfNeeded()
self.delegate?.thumbCurrentPosition(self.thumb.center.y)
},完成:无)
}
}

在xib中连接的panAction()方法。 平移手势识别器触发此功能,该功能通过检查状态来决定要执行的操作。

如果状态已.changed ,请移动拇指并与父视图进行通信。
如果state为.ended ,则首先检查拇指是否在底部,然后通知父视图。 如果没有,则将拇指返回其初始位置。

  @IBAction func panAction(){ 
如果panGesture.state == .changed {
slideThumb()
委托?.thumbCurrentPosition(self.thumb.center.y)
}
否则,如果panGesture.state == .ended {
如果isDestinationReached {
委托?.thumbReachedDestination()
}
其他{
returnInitialLocationAnimated(true)
}
}
}

3.实现创建视图的方法

在这一步中,我将展示通过从xib获取视图来准备视图的方法。

相关变量

  var contentView:UIView? 
var initialTopConstraint:CGFloat = 0.0

这些方法将从xib获取视图并将其应用于屏幕。 首先, awakeFromNib()由系统调用。 loadViewFromNib()从xib获取容器视图,然后arrangeView()设置该视图的AutoLayout属性,然后将其添加为子视图。 didMoveToWindows()是我设置一些初始属性的地方。

 覆盖func awakeFromNib(){ 
super.awakeFromNib()
rangeView()
} funcrangeView(){
守卫let view = loadViewFromNib()else {return}
view.frame =界限
view.autoresizingMask = [.flexibleWidth,.flexibleHeight]
self.addSubview(view)
contentView =视图
} func loadViewFromNib()-> UIView? {
let bundle = Bundle(for:type(of:self))
let nib = UINib(nibName:String(描述:KKVerticalSlider.self),bundle:bundle)
返回nib.instantiate(withOwner:self,选项:nil).first作为? UIView
}覆盖f​​unc didMoveToWindow(){
//确保拇指始终从顶部开始
self.thumbTopConstraint.constant = 0
self.initialTopConstraint = self.thumbTopConstraint.constant;
}

首先,我们需要在@IBDesignable开始时添加@IBDesignable

  @IBDesignable 
KKVerticalSlider类:UIView {
//您的代码...
}

如您从官方文档中看到的, prepareForInterfaceBuilder使我们的自定义视图生效。 在运行时不会调用此方法,而仅由Interface Builder调用。 那不是很酷吗?

对于界面构建器,我们在为设备执行操作时会安排视图。 在arrangeView()设置的contentView是容器视图。 您可以在第一章的第三步中看到它。 这些代码使我们的视图在情节提要中可见。

 覆盖func prepareForInterfaceBuilder(){ 
super.prepareForInterfaceBuilder()
rangeView()
contentView?.prepareForInterfaceBuilder()
}

聚一聚。 我有两个重要的技巧可以告诉你!

当您覆盖下面的方法时,请务必小心。 init(frame: CGRect)方法有助于我们以编程方式创建自定义视图。 例如,我正在如下使用它。 如果在情节提要板上出现渲染问题,请首先检查这些方法。

 覆盖init(frame:CGRect){ 
rangeView()
}是否需要init?(编码器aDecoder:NSCoder){
super.init(编码器:aDecoder)
}

另外,在加载视图时,请使用正确的捆绑包。 您可以在下面的StackOverflow线程中查看相关讨论。 这可以使您免于头痛。

无法呈现ClassName实例:代理在捆绑中装入笔尖时抛出异常

当Interface Builder呈现IBDesignable视图时,它使用一个助手应用程序来加载所有内容。 结果是…

stackoverflow.com


我正在使用IBInspectable使滑块角变圆。 实施起来非常简单。 您可以在下面的代码中找到如何使用它的屏幕截图。

  @IBInspectable var cornerRadius:CGFloat = 0 { 
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = cornerRadius> 0
}
}

在屏幕截图中,您可以在右侧菜单的“属性”检查器中看到“转角半径”变量。 您可以现场更改它。 当拐角半径从20更改为35时,您会看到差异。就是这样,容易吗?

我希望这个loooooooong教程为您提供有关IBDesignable以及如何准备自定义视图的足够信息。 如果您想深入研究代码,并且还看到如何将此自定义滑块应用于视图控制器(可以通过对滑块的操作将其关闭),则可以在我的Github页面上签出项目。

mkeremkeskin / SliderForDismissing

SliderForDismissing –通过使用自定义IBDesignable垂直滑块演示关闭视图控制器。

github.com

感谢您阅读本文。
如果您要添加某些内容或需要帮助,请添加评论。
再见!