在 Swift 中创建可重用视图并添加完成处理程序

Posted

技术标签:

【中文标题】在 Swift 中创建可重用视图并添加完成处理程序【英文标题】:Creating reusable View in Swift and adding a completion handler 【发布时间】:2019-02-11 15:43:09 【问题描述】:

我创建了一个 sliderView,并添加了一个 UIPanGesture,我创建了一个单独的 UIView 来处理这个滑块。我希望能够在需要它的不同 ViewController 中调用此 SliderView,但是当我尝试运行我的代码时它崩溃了。我还想传递一个完成处理程序来处理我需要它的 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
                )
            
        
    


【问题讨论】:

它在哪里崩溃了? 这里sliderImage.addGestureRecognizer(swipeGesture) King,sliderImage.addGestureRecognizer(swipeGesture) 中的崩溃是由于 sliderImagenil 值造成的。你在初始化对象时使用TripView(frame:)方法吗? 【参考方案1】:

您可以将完成处理程序添加为内部变量,或在初始化程序中传递一个。

例如:

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)

【讨论】:

委托人不清楚。我在问题中指定了将要执行的操作放在另一个视图控制器中的位置 您的视图控制器应该符合TripViewDelegate,然后您可以将您的视图控制器指定为TripView 的代理。有什么不清楚的地方?【参考方案2】:

您正在调用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

【讨论】:

@King 你也可以提到一些他不处理编码器初始化的情况。 @RobertDresler 是的,我注意到了,但我猜他并不真正支持。 @RobertDresler 我不太了解你 不是向前擦拭,而是向上移动并向下返回 @king 很抱歉,我无法解决您代码中的所有问题。这就是为什么问题必须非常具体的原因。

以上是关于在 Swift 中创建可重用视图并添加完成处理程序的主要内容,如果未能解决你的问题,请参考以下文章

在Vue中创建可重用的 Transition

在自定义视图单元中创建可绑定事件处理程序

如何在反应钩子中创建可重用状态?

如何在 laravel nova 中创建可重用的 vue 组件?

如何在ktor中创建可重用的拦截器?

如何在JSF中创建可重用的组件?