Масштабировать UIView только по горизонтали в UIScrollView

#ios #swift #uiscrollview #uiscrollviewdelegate

#iOS #swift #uiscrollview #uiscrollviewdelegate

Вопрос:

Я пробовал некоторые решения из других сообщений, но мне не удалось решить вопрос, который я собираюсь опубликовать.

У меня есть пользовательский UIView, который отображает звуковую волну и содержит UIScrollView. UIScrollView содержит основной UIView, который содержит два других пользовательских UIViews (маркер слева и справа) и UIImage view, в котором я отображаю звуковую волну.

 private func initialize(){

    print("(logClassName) initialize in Frame: (frame)")

    //ScrollView
    addSubview(scrollView)
    scrollView.constraintToSuperViewEdges()


    //WaveView
    scrollView.addSubview(waveView)
    waveView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
    waveView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0).isActive = true
    waveView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0).isActive = true
    waveView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true

    waveViewWidthConstraint = waveView.widthAnchor.constraint(equalToConstant: 1000)
    NSLayoutConstraint.activate([waveViewWidthConstraint])
    scrollView.contentSize = CGSize(width: waveViewWidthConstraint.constant, height: 0)

    //SoundWaveImageView
    waveView.addSubview(soundWaveImageView)
    soundWaveImageView.constraintToSuperViewEdges()


    //WaveView markers
    //TimeMarker
    waveView.addSubview(timeMarkerView)
    timeMarkerView.topAnchor.constraint(equalTo: waveView.topAnchor).isActive = true
    timeMarkerView.bottomAnchor.constraint(equalTo: waveView.bottomAnchor).isActive = true
    timeMarkerView.widthAnchor.constraint(equalToConstant: 1).isActive = true
    timerMarkerViewLeadingContraint = timeMarkerView.leadingAnchor.constraint(equalTo: waveView.leadingAnchor, constant: 20)
    NSLayoutConstraint.activate([timerMarkerViewLeadingContraint])


    //LeftMarker
    waveView.addSubview(leftMarkerView)
    leftMarkerView.topAnchor.constraint(equalTo: waveView.topAnchor).isActive = true
    leftMarkerView.bottomAnchor.constraint(equalTo: waveView.bottomAnchor).isActive = true
    leftMarkerView.widthAnchor.constraint(equalToConstant: leftMarkerView.triangleWidth).isActive = true
    leftMarkerViewLeadingConstraint = leftMarkerView.leadingAnchor.constraint(equalTo: waveView.leadingAnchor, constant: 0)
    NSLayoutConstraint.activate([leftMarkerViewLeadingConstraint])


    //RightMarker
    waveView.addSubview(rightMarkerView)
    rightMarkerView.topAnchor.constraint(equalTo: waveView.topAnchor).isActive = true
    rightMarkerView.bottomAnchor.constraint(equalTo: waveView.bottomAnchor).isActive = true
    rightMarkerView.widthAnchor.constraint(equalToConstant: rightMarkerView.triangleWidth).isActive = true
    rightMarkerViewLeadingConstraint = rightMarkerView.trailingAnchor.constraint(equalTo: waveView.leadingAnchor, constant: 50)
    NSLayoutConstraint.activate([rightMarkerViewLeadingConstraint])

}
  

Как видно, основным UIView является ограничение на края UIScrollView, а ограничением ширины является общая длина, которая изменяется в соответствии с потребностями. Функция, ответственная за это, следующая:

 private func updateWidthConstraintValue(_ newWidth:CGFloat){

    waveViewWidthConstraint.constant = newWidth
    scrollView.contentSize = CGSize(width: waveViewWidthConstraint.constant, height: 0)

}
  

При реализации масштабирования для UIScrollView это работает, но масштабируется как по вертикали, так и по горизонтали (как и ожидалось), но я хотел бы реализовать только по горизонтали. У меня пока это есть:

 func scrollViewDidZoom(_ scrollView: UIScrollView) {

    print("(logClassName) TEST -> scrollView did zoom (scrollView.zoomScale)")
    expandHorizontally(withScale: scrollView.zoomScale)
    //scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0)
    //scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height)
}

func viewForZooming(in scrollView: UIScrollView) -> UIView? {

    return waveView

}

private func expandHorizontally(withScale scale:CGFloat){

    let scaledWidth = currentWaveViewWidth * scale
    print("(logClassName) TEST -> scaledWidth = (scaledWidth)")

    updateWidthConstraintValue(scaledWidth)
    // Adjust content size and content offset
    scrollView.contentSize = CGSize(width: waveViewWidthConstraint.constant,
                                    height: 0);
    scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x,
                                       y: scrollView.contentOffset.y);

}
  

Переменные пользовательского интерфейса объявляются следующим образом:

 lazy private (set) var scrollView:UIScrollView = {

    let rtView = UIScrollView()

    rtView.translatesAutoresizingMaskIntoConstraints = false
    rtView.backgroundColor = .clear

    rtView.bounces = false
    rtView.bouncesZoom = false

    rtView.minimumZoomScale = 1
    rtView.maximumZoomScale = 3

    rtView.delegate = self

    return rtView

}()

private (set) lazy var waveView:UIView = {

    let rtView = UIView()

    rtView.translatesAutoresizingMaskIntoConstraints = false
    rtView.backgroundColor = .clear
    rtView.addGestureRecognizer(UITapGestureRecognizer(target: self,
                                                       action: #selector(waveViewDidTouch)))

    return rtView

}()
private (set) var waveViewWidthConstraint = NSLayoutConstraint()

private (set) lazy var soundWaveImageView:UIImageView = {

    let rtView = UIImageView()

    rtView.translatesAutoresizingMaskIntoConstraints = false

    return rtView

}()

private (set) lazy var timeMarkerView:UIView = {
    let rtView = UIView()

    rtView.translatesAutoresizingMaskIntoConstraints = false
    rtView.backgroundColor = UIColor.AppColors.defaultTint

    return rtView
}()
private var timerMarkerViewLeadingContraint = NSLayoutConstraint()

private (set) lazy var leftMarkerView:MarkerView = {

    let rtView = MarkerView(title: AppHelper.printLocalized(withKey: "messages.start", targetSpecific: false), markerDirection: .right)

    rtView.translatesAutoresizingMaskIntoConstraints = false
    rtView.delegate = self

    return rtView

}()
private var leftMarkerViewLeadingConstraint = NSLayoutConstraint()

private (set) lazy var rightMarkerView:MarkerView = {

    let rtView = MarkerView(title: AppHelper.printLocalized(withKey: "messages.end", targetSpecific: false), markerDirection: .left)

    rtView.translatesAutoresizingMaskIntoConstraints = false
    rtView.delegate = self

    return rtView

}()
private var rightMarkerViewLeadingConstraint = NSLayoutConstraint()
  

Я попытался поместить всю необходимую информацию, поскольку это большой класс.

Комментарии:

1. Я не проверял весь ваш код, но обычный способ предотвратить вертикальную прокрутку ScrollView — это ограничить его содержимое родительским представлением ScrollView (ограничения верхнего и нижнего якорей). Допустим, у вас есть parentView> ScrollView> contentsView : ScrollView должен быть ограничен краями parentView, а contentsView должен быть ограничен верхними и нижними привязками parentView.

2. спасибо за ваш ответ. Я вижу, что я ограничиваю только верхнюю часть и даю равную высоту. Тогда следует удалить ограничение равной высоты и и снизу?

3. Я думаю, да… На самом деле ScrollView должен быть ограничен краями parentView, а contentsView должен быть ограничен краями ScrollView И верхними и нижними привязками parentView.

4. Итак, waveView (оболочка маркеров и UIImageView) ограничен краями scrollview и ограничен верхней и нижней частью оболочки ScrollView, верно? Значит, я что-то делаю со своим делегатом?

5. Ну, мой вопрос был о масштабировании, поэтому он не работает…

Ответ №1:

Я нашел временное решение, спасибо LaurentMaquet. В какой-то момент я опубликую окончательный вариант soundWaveView, поскольку он находится в разработке на данный момент.

Внесенные изменения являются:

1- Создайте пользовательский класс для soundWave.

 class WaveView:UIView{
    var unzoomedViewHeight: CGFloat = 0

    override var transform: CGAffineTransform{
        get{ return super.transform }
        set{
            var t = newValue
            t.d = 1.0
            t.ty = (1.0 - t.a) * unzoomedViewHeight/2
            super.transform = t
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        unzoomedViewHeight = frame.size.height
        print("(logClassName) did layout to (AppHelper.traitStatus) -> frame = (frame)")

    }

}
  

2- Сделайте мой soundView var SoundView

 private (set) lazy var waveView:WaveView = {

    let rtView = WaveView()

    rtView.translatesAutoresizingMaskIntoConstraints = false
    rtView.backgroundColor = .clear
    rtView.addGestureRecognizer(UITapGestureRecognizer(target: self,
                                                       action: #selector(waveViewDidTouch)))

    return rtView

}()
  

Результат

введите описание изображения здесь

введите описание изображения здесь