在 Swift 中创建可重用视图并添加完成处理程序
Creating reusable View in Swift and adding a completion handler
我创建了一个 sliderView,并添加了一个 UIPanGesture,我创建了一个单独的 UIView 来处理这个滑块。我希望能够在我需要的不同 ViewController 中调用此 SliderView 但是当我尝试 运行 我的代码时 IT crashed.I 也想传递一个完成处理程序来处理我所在的 ViewCONtrollers 中的事件会需要它。我试图通过创建一个 UIView 并在多个屏幕中使用它来遵循 DRY 过程。以下是我目前的代码
class TripView: UIView {
var shouldSetupConstraints = true
var startingFrame: CGRect?
var sliderView: UIView!
var sliderImage: UIImageView!
let screenSize = UIScreen.main.bounds
override init(frame: CGRect){
super.init(frame: frame)
swipeFunc()
sliderView = UIImageView(frame: CGRect.zero)
sliderView.backgroundColor = UIColor.green
sliderView.autoSetDimension(.height, toSize: screenSize.width / 6)
self.addSubview(sliderView)
sliderImage = UIImageView(frame: CGRect.zero)
sliderImage.backgroundColor = UIColor.clear
sliderImage.image = UIImage(named: "icons8-double_right_filled.png")
sliderImage.contentMode = .scaleAspectFit
sliderImage.autoSetDimension(.width, toSize: screenSize.width / 6)
sliderImage.autoSetDimension(.height, toSize: screenSize.width / 6)
self.addSubview(sliderImage)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func updateConstraints() {
if(shouldSetupConstraints) {
sliderView.autoPinEdgesToSuperviewSafeArea(with: UIEdgeInsets.zero, excludingEdge: .bottom)
sliderImage.autoPinEdge(toSuperviewEdge: .left)
sliderImage.autoPinEdge(.bottom, to: .bottom, of: sliderView, withOffset: 0.0)
shouldSetupConstraints = false
}
super.updateConstraints()
}
}
extension TripView {
private func swipeFunc() {
let swipeGesture = UIPanGestureRecognizer(target: self, action: #selector(acknowledgeSwiped(sender:)))
sliderImage.addGestureRecognizer(swipeGesture)
sliderImage.isUserInteractionEnabled = true
swipeGesture.delegate = self as? UIGestureRecognizerDelegate
}
@objc func acknowledgeSwiped(sender: UIPanGestureRecognizer) {
if let sliderView = sender.view {
let translation = sender.translation(in: sliderView)
switch sender.state {
case .began:
startingFrame = sliderImage.frame
fallthrough
case .changed:
if let startFrame = startingFrame {
var movex = translation.x
if movex < -startFrame.origin.x { movex = -startFrame.origin.x }
let xMax = sliderView.frame.width - startFrame.origin.x - startFrame.width
if movex > xMax {
movex = xMax
// I WANT TO PASS A COMPLETION HANDLER HERE. TO BE ABLE TO HANDLE OTHER EVENTS IN VIEWCONTROLLERS
}
var movey = translation.y
if movey < -startFrame.origin.y { movey = -startFrame.origin.y }
let yMax = sliderView.frame.height - startFrame.origin.y - startFrame.height
if movey > yMax {
movey = yMax
}
sliderView.transform = CGAffineTransform(translationX: movex, y: movey)
}
default: // .ended and others:
UIView.animate(withDuration: 0.1, animations: {
sliderView.transform = CGAffineTransform.identity
})
}
}
}
}
您正在调用 swipeFunc
,它在 sliderImage
初始化之前访问 sliderImage
。
您必须将 swipeFunc()
移动到 init
方法的末尾。
实际上,没有必要使用隐式解包选项。您可以使用非可选的来防止崩溃:
let sliderView: UIView = UIImageView(frame: CGRect.zero)
let sliderImage: UIImageView = UIImageView(frame: CGRect.zero)
因为你实际上有两个初始化器,你可能应该初始化两者中的所有内容:
let sliderView: UIView = UIImageView(frame: CGRect.zero)
let sliderImage: UIImageView = UIImageView(frame: CGRect.zero)
override init(frame: CGRect){
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
sliderView.backgroundColor = UIColor.green
sliderView.autoSetDimension(.height, toSize: screenSize.width / 6)
self.addSubview(sliderView)
sliderImage.backgroundColor = UIColor.clear
sliderImage.image = UIImage(named: "icons8-double_right_filled.png")
sliderImage.contentMode = .scaleAspectFit
sliderImage.autoSetDimension(.width, toSize: screenSize.width / 6)
sliderImage.autoSetDimension(.height, toSize: screenSize.width / 6)
self.addSubview(sliderImage)
swipeFunc()
}
关于回调,只需声明一个 属性:
class TripView: UIView {
var onChange: (() -> Void)?
}
并且在每次更改电话时:
onChange?()
然后在您的控制器中您可以:
var tripView: TripView = ...
tripView.onChange = {
// handle the event
}
您可以将完成处理程序添加为内部变量,或在初始化程序中传递一个。
例如:
class TripView: UIView {
typealias Callback = () -> ()
var callback: Callback
...
}
然后在你想触发回调的地方,直接调用:
callback()
或者您可以定义一个委托
protocol TripViewDelegate {
func didDoSomething(for view: TripView)
}
class TripView: UIView {
weak var tripViewDelegate: TripViewDelegate?
// ...
}
然后调用
tripViewDelegate?.didDoSomething(for: self)
我创建了一个 sliderView,并添加了一个 UIPanGesture,我创建了一个单独的 UIView 来处理这个滑块。我希望能够在我需要的不同 ViewController 中调用此 SliderView 但是当我尝试 运行 我的代码时 IT crashed.I 也想传递一个完成处理程序来处理我所在的 ViewCONtrollers 中的事件会需要它。我试图通过创建一个 UIView 并在多个屏幕中使用它来遵循 DRY 过程。以下是我目前的代码
class TripView: UIView {
var shouldSetupConstraints = true
var startingFrame: CGRect?
var sliderView: UIView!
var sliderImage: UIImageView!
let screenSize = UIScreen.main.bounds
override init(frame: CGRect){
super.init(frame: frame)
swipeFunc()
sliderView = UIImageView(frame: CGRect.zero)
sliderView.backgroundColor = UIColor.green
sliderView.autoSetDimension(.height, toSize: screenSize.width / 6)
self.addSubview(sliderView)
sliderImage = UIImageView(frame: CGRect.zero)
sliderImage.backgroundColor = UIColor.clear
sliderImage.image = UIImage(named: "icons8-double_right_filled.png")
sliderImage.contentMode = .scaleAspectFit
sliderImage.autoSetDimension(.width, toSize: screenSize.width / 6)
sliderImage.autoSetDimension(.height, toSize: screenSize.width / 6)
self.addSubview(sliderImage)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func updateConstraints() {
if(shouldSetupConstraints) {
sliderView.autoPinEdgesToSuperviewSafeArea(with: UIEdgeInsets.zero, excludingEdge: .bottom)
sliderImage.autoPinEdge(toSuperviewEdge: .left)
sliderImage.autoPinEdge(.bottom, to: .bottom, of: sliderView, withOffset: 0.0)
shouldSetupConstraints = false
}
super.updateConstraints()
}
}
extension TripView {
private func swipeFunc() {
let swipeGesture = UIPanGestureRecognizer(target: self, action: #selector(acknowledgeSwiped(sender:)))
sliderImage.addGestureRecognizer(swipeGesture)
sliderImage.isUserInteractionEnabled = true
swipeGesture.delegate = self as? UIGestureRecognizerDelegate
}
@objc func acknowledgeSwiped(sender: UIPanGestureRecognizer) {
if let sliderView = sender.view {
let translation = sender.translation(in: sliderView)
switch sender.state {
case .began:
startingFrame = sliderImage.frame
fallthrough
case .changed:
if let startFrame = startingFrame {
var movex = translation.x
if movex < -startFrame.origin.x { movex = -startFrame.origin.x }
let xMax = sliderView.frame.width - startFrame.origin.x - startFrame.width
if movex > xMax {
movex = xMax
// I WANT TO PASS A COMPLETION HANDLER HERE. TO BE ABLE TO HANDLE OTHER EVENTS IN VIEWCONTROLLERS
}
var movey = translation.y
if movey < -startFrame.origin.y { movey = -startFrame.origin.y }
let yMax = sliderView.frame.height - startFrame.origin.y - startFrame.height
if movey > yMax {
movey = yMax
}
sliderView.transform = CGAffineTransform(translationX: movex, y: movey)
}
default: // .ended and others:
UIView.animate(withDuration: 0.1, animations: {
sliderView.transform = CGAffineTransform.identity
})
}
}
}
}
您正在调用 swipeFunc
,它在 sliderImage
初始化之前访问 sliderImage
。
您必须将 swipeFunc()
移动到 init
方法的末尾。
实际上,没有必要使用隐式解包选项。您可以使用非可选的来防止崩溃:
let sliderView: UIView = UIImageView(frame: CGRect.zero)
let sliderImage: UIImageView = UIImageView(frame: CGRect.zero)
因为你实际上有两个初始化器,你可能应该初始化两者中的所有内容:
let sliderView: UIView = UIImageView(frame: CGRect.zero)
let sliderImage: UIImageView = UIImageView(frame: CGRect.zero)
override init(frame: CGRect){
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
sliderView.backgroundColor = UIColor.green
sliderView.autoSetDimension(.height, toSize: screenSize.width / 6)
self.addSubview(sliderView)
sliderImage.backgroundColor = UIColor.clear
sliderImage.image = UIImage(named: "icons8-double_right_filled.png")
sliderImage.contentMode = .scaleAspectFit
sliderImage.autoSetDimension(.width, toSize: screenSize.width / 6)
sliderImage.autoSetDimension(.height, toSize: screenSize.width / 6)
self.addSubview(sliderImage)
swipeFunc()
}
关于回调,只需声明一个 属性:
class TripView: UIView {
var onChange: (() -> Void)?
}
并且在每次更改电话时:
onChange?()
然后在您的控制器中您可以:
var tripView: TripView = ...
tripView.onChange = {
// handle the event
}
您可以将完成处理程序添加为内部变量,或在初始化程序中传递一个。
例如:
class TripView: UIView {
typealias Callback = () -> ()
var callback: Callback
...
}
然后在你想触发回调的地方,直接调用:
callback()
或者您可以定义一个委托
protocol TripViewDelegate {
func didDoSomething(for view: TripView)
}
class TripView: UIView {
weak var tripViewDelegate: TripViewDelegate?
// ...
}
然后调用
tripViewDelegate?.didDoSomething(for: self)