首页 > 解决方案 > 如何从整个应用程序的弹出窗口中拖动按钮?(迅速)

问题描述

在我的应用程序中,我正在显示一个显示自定义 UIView 的弹出窗口,允许用户使用各种滑块选择颜色。我还想实现一个“吸管”工具,用户可以点击并按住然后拖动以从应用程序中可见的任何内容中选择颜色。在我的自定义 UIView 中,我添加了一个UIPanGestureRecognizer指向该handlePan方法的按钮:

var eyedropperStartLocation = CGPoint.zero
@objc func handlePan(recognizer: UIPanGestureRecognizer) {

    // self is a custom UIView that contains my color selection
    // sliders and is placed inside a UITableView that's in
    // the popover.
    let translation = recognizer.translation(in: self)
    if let view = recognizer.view {

        switch recognizer.state {
        case .began:
            eyedropperStartLocation = view.center

        case .ended, .failed, .cancelled:
            view.center = eyedropperStartLocation
            return

        default: break

        }
        view.center = CGPoint(x: view.center.x + translation.x,
                              y: view.center.y + translation.y)
        recognizer.setTranslation(CGPoint.zero, in: self)

    }
}

我可以拖动按钮并更改位置,但是我有两个问题:

  1. 吸管按钮并不总是在其他项目的前面,即使在弹出框内或弹出框内的自定义 UIView 内
  2. 滴管按钮在弹出框范围之外消失

如何让按钮始终可见,包括在弹出框之外?我想检测用户在应用程序中放开它的位置,以便确定它的颜色。

标签: iosswiftuipangesturerecognizer

解决方案


我想出了如何做到这一点,所以我会回答我自己的问题。我没有在弹出窗口中移动视图/按钮,而是创建了一个新的 UIImageView 并将其添加到应用程序的窗口中,让它跨越整个应用程序。原始按钮保持原样 - 您可以轻松更改其状态以使其看起来不同,或者如果您愿意,可以隐藏它。

您也可以使用 Interface Builder 绑定到@IBActions,但我只是在代码中完成了所有操作。clickButton 方法开始了一些事情,但计算窗口中的位置并将其放在屏幕上。handlePan 方法进行翻译并让您移动它。

下面的所有代码都是 swift 4 并且在 XCode 9.4.1 中工作(假设我没有引入任何拼写错误):

// Call this from all your init methods so it will always happen
func commonInit() {
    let panner = UIPanGestureRecognizer(target: self, action: #selector(handlePan(recognizer:)))
    theButton.addGestureRecognizer(panner)
    theButton.addTarget(self, action: #selector(clickButton), for: .touchDown)
        eyedropperButton.addTarget(self, action: #selector(unclickButton), for: .touchUpInside)
        eyedropperButton.addTarget(self, action: #selector(unclickButton), for: .touchUpOutside)
}

var startLocation = CGPoint.zero
lazy var floatingView: UIImageView = {
    let view = UIImageView(image: UIImage(named: "imagename"))
    view.backgroundColor = UIColor.blue
    return view
}()


// When the user clicks button - we create the image and put it on the screen
// this makes the action seem faster vs waiting for the panning action to kick in
@objc func clickButton() {

    guard let app = UIApplication.shared.delegate as? AppDelegate, let window = app.window else { return }

    // We ask the button what it's bounds are within it's own coordinate system and convert that to the
    // Window's coordinate system and set the frame on the floating view. This makes the new view overlap the
    // button exactly.
    floatingView.frame = theButton.convert(theButton.bounds, to: nil)

    window.addSubview(floatingView)

    // Save the location so we can translate it as part of the pan actions
    startLocation = floatingView.center
}

// This is here to handle the case where the user didn't move enough to kick in the panGestureRecognizer and cancel the action
@objc func unclickButton() {
    floatingView.removeFromSuperview()
}

@objc func handlePan(recognizer: UIPanGestureRecognizer) {
    let translation = recognizer.translation(in: self)

    switch recognizer.state {
    case .ended, .failed, .cancelled:
        doSomething()
        floatingView.removeFromSuperview()
        return
    default: break

    }

    // This section is called for any pan state except .ended, .failed and .cancelled
    floatingView.center = CGPoint(x: floatingView.center.x + translation.x,
                                  y: floatingView.center.y + translation.y)
    recognizer.setTranslation(CGPoint.zero, in: self)
}

推荐阅读