Vertical ScrollView не прокручивается (без раскадровки) - PullRequest
0 голосов
/ 02 августа 2020

Мне нужна помощь в создании прокрутки без раскадровки. Вот мой код для настройки прокрутки; Я не устанавливаю contentSize для Scroll View, потому что я бы хотел, чтобы размер содержимого прокрутки был динамическим c, в зависимости от количества текста в TextView. Вместо этого я попытался добавить contentView в Scroll View и добавил все свои элементы пользовательского интерфейса в contentView. Любая помощь будет принята с благодарностью.

import Foundation
import UIKit
import UITextView_Placeholder

class ComposerVC: UIViewController {
  
  private var scrollView: UIScrollView = {
    let scrollView = UIScrollView(frame: UIScreen.main.bounds)
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    return scrollView
  }()
  
  private var contentView: UIView = {
    let content = UIView()
    content.translatesAutoresizingMaskIntoConstraints = false
    return content
  }()
  
  private var title: UITextView = {
    let title = UITextView()
    title.translatesAutoresizingMaskIntoConstraints = false
    title.placeholder = "Untitled"
    title.textColor = UIColor(hexString: "#50E3C2")
    title.font = UIFont(name: "Rubik-BoldItalic", size: 32)
    title.backgroundColor = .clear
    title.isScrollEnabled = false
    return title
  }()
  
  private var divider: UIView = {
    let divider = UIView()
    divider.translatesAutoresizingMaskIntoConstraints = false
    divider.backgroundColor = UIColor(hexString: "#50E3C2")
    return divider
  }()
  
  private var content: UITextView = {
    let title = UITextView()
    title.translatesAutoresizingMaskIntoConstraints = false
    title.placeholder = "Begin writing here..."
    title.textColor = .white
    title.font = UIFont(name: "Avenir-Book", size: 15)
    title.backgroundColor = .clear
    title.isScrollEnabled = false
    return title
  }()
  
  override func viewDidLoad() {
    setupUI()
    setupUIConstraints()
    title.delegate = self
  }
  
  private func setupUI() {
    view.backgroundColor = UIColor(hexString: "#131415")
    view.addSubview(scrollView)
    scrollView.addSubview(contentView)
    contentView.addSubview(title)
    contentView.addSubview(divider)
    contentView.addSubview(content)
  }
  
  private func setupUIConstraints() {
    
    scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    
    contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
    contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
    contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
    contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
    contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    
    title.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 95).isActive = true
    title.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
    title.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
    
    divider.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 15).isActive = true
    divider.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
    divider.heightAnchor.constraint(equalToConstant: 1).isActive = true
    divider.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.8).isActive = true
    
    content.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 15).isActive = true
    content.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
    content.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
  }
}

extension ComposerVC: UITextViewDelegate {
  func textViewDidChange(_ textView: UITextView) {
    let fixedWidth = textView.frame.size.width
    let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
    textView.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
  }
}

Ответы [ 2 ]

1 голос
/ 03 августа 2020

Несколько советов:

  1. Не используйте существующие имена для переменных ... с вашим кодом как есть, private var title: UITextView вызывает проблемы (title уже является свойством контроллера представления) .
  2. Используйте имена переменных, которые подразумевают объект ... например, titleTextView и contentTextView вместо title и content
  3. Во время разработки - особенно когда вы работаете над layout - придайте элементам пользовательского интерфейса контрастные цвета фона, чтобы вы могли легко видеть их фреймы во время выполнения.
  4. При использовании представлений, созданных с помощью кода, установите .clipsToBounds = true ... добавлено, вы знаете, что в фреймах / ограничениях чего-то не хватает.

У меня нет вашего UITextView_Placeholder импорта, но это ни на что не повлияет ...

Итак сначала измените свой viewDidLoad() на этот:

override func viewDidLoad() {
    setupUI()
    setupUIConstraints()
    titleTextView.delegate = self
    
    // contrasting colors during development
    scrollView.backgroundColor = .red
    titleTextView.backgroundColor = .yellow
    contentTextView.backgroundColor = .green
    divider.backgroundColor = .blue
    contentView.backgroundColor = .cyan
}

Когда вы запустите его, вы должны увидеть (фон прокрутки красный, и это без заполнителя):

enter image description here

It looks correct, except there's no cyan-colored contentView.

Now, clip the subviews of contentView:

private var contentView: UIView = {
    let content = UIView()
    content.translatesAutoresizingMaskIntoConstraints = false
    // add this line
    content.clipsToBounds = true
    return content
}()

The result:

enter image description here

Where did everything go? Well, we didn't see the cyan contentView and now we don't see any of its subviews ... If we use Debug View Hierarchy we can find out contentView has a Height of Zero.

Fix that by constraining the bottom of the second text view in setupUIConstraints() (I've renamed it to contentTextView instead of just content):

    contentTextView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -95).isActive = true

and we get:

enter image description here

Now the Height of the cyan contentView is controlled by correctly setup constraints of its subviews.

As a side note: with constraints setup properly, and scrolling disabled for the text views, you do not need your:

extension ComposerVC: UITextViewDelegate {
  //func textViewDidChange(_ textView: UITextView) {
  //...
  //}
}

The text view will automatically size itself to its text:

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

Вот полный отредактированный код:

class ComposerVC: UIViewController {
    
    private var scrollView: UIScrollView = {
        let scrollView = UIScrollView(frame: UIScreen.main.bounds)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        return scrollView
    }()
    
    private var contentView: UIView = {
        let content = UIView()
        content.translatesAutoresizingMaskIntoConstraints = false
        // add this line so we know if the constraints are set correctly
        content.clipsToBounds = true
        return content
    }()
    
    private var titleTextView: UITextView = {
        let title = UITextView()
        title.translatesAutoresizingMaskIntoConstraints = false
//      title.placeholder = "Untitled"
        title.textColor = UIColor(hexString: "#50E3C2")
        title.font = UIFont(name: "Rubik-BoldItalic", size: 32)
        title.backgroundColor = .clear
        title.isScrollEnabled = false
        return title
    }()
    
    private var divider: UIView = {
        let divider = UIView()
        divider.translatesAutoresizingMaskIntoConstraints = false
        divider.backgroundColor = UIColor(hexString: "#50E3C2")
        return divider
    }()
    
    private var contentTextView: UITextView = {
        let title = UITextView()
        title.translatesAutoresizingMaskIntoConstraints = false
//      title.placeholder = "Begin writing here..."
        title.textColor = .white
        title.font = UIFont(name: "Avenir-Book", size: 15)
        title.backgroundColor = .clear
        title.isScrollEnabled = false
        return title
    }()
    
    override func viewDidLoad() {
        setupUI()
        setupUIConstraints()
        titleTextView.delegate = self
        
        // contrasting colors during development
        scrollView.backgroundColor = .red
        titleTextView.backgroundColor = .yellow
        contentTextView.backgroundColor = .green
        divider.backgroundColor = .blue
        contentView.backgroundColor = .cyan
    }
    
    private func setupUI() {
        view.backgroundColor = UIColor(hexString: "#131415")
        view.addSubview(scrollView)
        scrollView.addSubview(contentView)
        contentView.addSubview(titleTextView)
        contentView.addSubview(divider)
        contentView.addSubview(contentTextView)
    }
    
    private func setupUIConstraints() {
        
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        
        contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
        contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
        contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
        
        titleTextView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 95).isActive = true
        titleTextView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
        titleTextView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
        
        divider.topAnchor.constraint(equalTo: titleTextView.bottomAnchor, constant: 15).isActive = true
        divider.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        divider.heightAnchor.constraint(equalToConstant: 1).isActive = true
        divider.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.8).isActive = true
        
        contentTextView.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 15).isActive = true
        contentTextView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
        contentTextView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
        
        contentTextView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -95).isActive = true
    
    }
}

extension ComposerVC: UITextViewDelegate {
//  func textViewDidChange(_ textView: UITextView) {
//      let fixedWidth = textView.frame.size.width
//      let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
//      textView.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
//  }
}
0 голосов
/ 02 августа 2020

Предполагая, что вы используете iOS 11+, ваш contentView должен иметь привязки, привязанные к contentLayoutGuide scrollView. вот так:

contentView
    .leadingAnchor
    .constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor).isActive = true
contentView
    .topAnchor
    .constraint(equalTo: scrollView.contentLayoutGuide.topAnchor).isActive = true
contentView
    .trailingAnchor
    .constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor).isActive = true
contentView
    .bottomAnchor
    .constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor).isActive = true

Кроме того, его ширина должна быть ограничена frameLayoutGuide scrollView, а не шириной представления, например:

contentView
.widthAnchor
.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor).isActive = true

Это должно заставить scrollView определять размер содержимого по размеру.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...