Извлечение childView и изменение его положения внутри нового parentView - PullRequest
0 голосов
/ 14 января 2020

Я пытаюсь скопировать представление из stackView, встроенного в scrollView, а затем изменить положение указанного представления в том же месте, но в другом представлении на том же уровне в иерархии представления, что и scrollView.

Эффект, которого я пытаюсь достичь, заключается в том, что я анимирую удаление вида, где вид будет наложен на другой вид, в то время как scrollView будет прокручиваться вверх, и новый вид будет добавляться в stackView все время, пока вид, который был разорван, исчезает.

К сожалению, достижение этого эффекта остается труднодостижимым, поскольку rippedView находится в позиции (x: 0, y: 0). Когда я пытаюсь наложить новый кадр на это представление, это сложно, потому что я угадываю пиксель совершенно правильный кадр. Вот немного кода из моего viewController:

/* 
 I tried to make insertionView and imposeView have the same dimensions as the scrollView and 
 the stackView respectively as I thought if the rippedView’s original superView is the same 
 dimensions as it’s new superView, the rippedView would be positioned in the same place 
 without me needing to alter its frame.
 */


let insertionView = UIView(frame: scrollView.frame)
let imposeView = UIView(frame: stackView.frame) 

rippedView.removeFromSuperview()
insertionView.addSubview(imposeView)
imposeView.addSubview(rippedView)

let newFrame = CGRect(x: 0, y: 450, width: rippedView.intrinsicContentSize.width, height: 
rippedView.intrinsicContentSize.height)

rippedView.frame = newFrame
self.view.addSubview(insertionView)

Ответы [ 2 ]

1 голос
/ 14 января 2020

Проблема, с которой вы столкнулись, вероятнее всего из-за упорядоченных подпредставлений stackView с .translatesAutoresizingMaskIntoConstraints, установленным на false. Я полагаю, что это происходит автоматически, когда вы добавляете представление в stackView, если не указано иное.

Упорядоченные подпредставления stackView имеют координаты относительно самого stackView. Таким образом, первый вид будет на 0,0. Поскольку вы добавляете представление «контейнер» с тем же фреймом, что и stackView, вы можете использовать то же координатное пространство ... но вам нужно будет включить .translatesAutoresizingMaskIntoConstraints.

Попробуйте это так:

@objc func btnTapped(_ sender: Any?) -> Void {

    // get a reference to the 3rd arranged subview in the stack view
    let rippedView = stackView.arrangedSubviews[2]

    // local var holding the rippedView frame (as set by the stackView)
    // get it before moving view from stackView
    let r = rippedView.frame

    // instantiate views
    let insertionView = UIView(frame: scrollView.frame)
    let imposeView = UIView(frame: stackView.frame)

    // add imposeView to insertionView
    insertionView.addSubview(imposeView)

    // add insertionView to self.view
    self.view.addSubview(insertionView)

    // move rippedView from stackView to imposeView
    imposeView.addSubview(rippedView)

    // just to make it easy to see...
    rippedView.backgroundColor = .green

    // set to TRUE
    rippedView.translatesAutoresizingMaskIntoConstraints = true

    // set the frame
    rippedView.frame = r
}

Вот полный пример класса, который вы можете запустить напрямую (просто назначьте его контроллеру представления):

class RipViewViewController: UIViewController {

    let aButton: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .red
        v.setTitle("Testing", for: .normal)
        return v
    }()

    let scrollView: UIScrollView = {
        let v = UIScrollView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .systemBlue
        return v
    }()

    let stackView: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .vertical
        v.spacing = 8
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(aButton)

        view.addSubview(scrollView)
        scrollView.addSubview(stackView)

        let g = view.safeAreaLayoutGuide
        let sg = scrollView.contentLayoutGuide

        NSLayoutConstraint.activate([

            aButton.topAnchor.constraint(equalTo: g.topAnchor, constant: 16.0),
            aButton.centerXAnchor.constraint(equalTo: g.centerXAnchor, constant: 0.0),

            scrollView.topAnchor.constraint(equalTo: aButton.bottomAnchor, constant: 40.0),
            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0),

            stackView.topAnchor.constraint(equalTo: sg.topAnchor, constant: 40.0),
            stackView.leadingAnchor.constraint(equalTo: sg.leadingAnchor, constant: 20.0),
            stackView.trailingAnchor.constraint(equalTo: sg.trailingAnchor, constant: 20.0),
            stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -40.0),

            stackView.bottomAnchor.constraint(equalTo: sg.bottomAnchor, constant: 20.0),

        ])

        for i in 1...5 {
            let l = UILabel()
            l.backgroundColor = .cyan
            l.textAlignment = .center
            l.text = "Label \(i)"
            stackView.addArrangedSubview(l)
        }

        aButton.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)

    }

    @objc func btnTapped(_ sender: Any?) -> Void {

        // get a reference to the 3rd arranged subview in the stack view
        let rippedView = stackView.arrangedSubviews[2]

        // local var holding the rippedView frame (as set by the stackView)
        // get it before moving view from stackView
        let r = rippedView.frame

        // instantiate views
        let insertionView = UIView(frame: scrollView.frame)
        let imposeView = UIView(frame: stackView.frame)

        // add imposeView to insertionView
        insertionView.addSubview(imposeView)

        // add insertionView to self.view
        self.view.addSubview(insertionView)

        // move rippedView from stackView to imposeView
        imposeView.addSubview(rippedView)

        // just to make it easy to see...
        rippedView.backgroundColor = .green

        // set to TRUE
        rippedView.translatesAutoresizingMaskIntoConstraints = true

        // set the frame
        rippedView.frame = r

    }

}
1 голос
/ 14 января 2020

Перед удалением rippedView, получите фактический кадр:

let newFrame = self.view.convert(rippedView.bounds, from: rippedView)
...