Как я могу расположить эти элементы UIView справа, используя CGRect для позиционирования - PullRequest
0 голосов
/ 08 января 2020

У меня есть подкласс UIView, который позволяет мне создать группу «тегов» для нижнего колонтитула некоторого содержимого. На данный момент, однако, они выровнены по левому краю, я бы хотел, чтобы они располагались справа.

enter image description here

У меня есть детская площадка ниже этого должен запускаться снимок экрана, который вы видите.

Позиция установлена ​​в методе layoutSubviews CloudTagView.

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

import UIKit
import PlaygroundSupport

// CLOUD VIEW WRAPPER - THIS IS THE CONTAINER FOR THE TAGS AND SETS UP THEIR FRAME
class CloudTagView: UIView {

  weak var delegate: TagViewDelegate?

  override var intrinsicContentSize: CGSize {
    return frame.size
  }

  var removeOnDismiss = true
  var resizeToFit = true

  var tags = [TagView]() {
    didSet {
      layoutSubviews()
    }
  }
  var padding = 5 {
    didSet {
      layoutSubviews()
    }
  }
  var maxLengthPerTag = 0 {
    didSet {
      layoutSubviews()
    }
  }

  public override init(frame: CGRect) {
    super.init(frame: frame)

    isUserInteractionEnabled = true
    clipsToBounds = true
  }

  public required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    isUserInteractionEnabled = true
    clipsToBounds = true
  }

  override func layoutSubviews() {
    for tag in subviews {
      tag.removeFromSuperview()
    }

    var xAxis = padding
    var yAxis = padding
    var maxHeight = 0

    for (index, tag) in tags.enumerated() {

      setMaxLengthIfNeededIn(tag)

      tag.delegate = self

      if index == 0 {
        maxHeight = Int(tag.frame.height)
      }else{
        let expectedWidth = xAxis + Int(tag.frame.width) + padding

        if expectedWidth > Int(frame.width) {
          yAxis += maxHeight + padding
          xAxis = padding
          maxHeight = Int(tag.frame.height)
        }

        if Int(tag.frame.height) > maxHeight {
          maxHeight = Int(tag.frame.height)
        }
      }
      tag.frame = CGRect(x: xAxis, y: yAxis, width: Int(tag.frame.size.width), height: Int(tag.frame.size.height))
      addSubview(tag)
      tag.layoutIfNeeded()
      xAxis += Int(tag.frame.width) + padding
    }

    if resizeToFit {
      frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: CGFloat(yAxis + maxHeight + padding))
    }
  }

  // MARK: Methods
  fileprivate func setMaxLengthIfNeededIn(_ tag: TagView) {
    if maxLengthPerTag > 0 && tag.maxLength != maxLengthPerTag {
      tag.maxLength = maxLengthPerTag
    }
  }

}


// EVERYTHING BELOW HERE IS JUST SETUP / REQUIRED TO RUN IN PLAYGROUND

class ViewController:UIViewController{

  let cloudView: CloudTagView = {
    let view = CloudTagView(frame: .zero)
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
  }()

  override func viewDidLoad() {
    super.viewDidLoad()


    let tags = ["these", "are", "my", "tags"]

    tags.forEach { tag in
      let t = TagView(text: tag)
      t.backgroundColor = .darkGray
      t.tintColor = .white
      cloudView.tags.append(t)
    }

    view.backgroundColor = .white

    view.addSubview(cloudView)
    NSLayoutConstraint.activate([
      cloudView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
      cloudView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
      cloudView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
      cloudView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
    ])


  }

}

// Tag View

class TagView: UIView {

  weak var delegate: TagViewDelegate?

  var text = "" {
    didSet {
      layoutSubviews()
    }
  }
  var marginTop = 5 {
    didSet {
      layoutSubviews()
    }
  }
  var marginLeft = 10 {
    didSet {
      layoutSubviews()
    }
  }
  var iconImage = UIImage(named: "close_tag_2", in: Bundle(for: CloudTagView.self), compatibleWith: nil) {
    didSet {
      layoutSubviews()
    }
  }
  var maxLength = 0 {
    didSet {
      layoutSubviews()
    }
  }

  override var backgroundColor: UIColor? {
    didSet {
      layoutSubviews()
    }
  }

  override var tintColor: UIColor? {
    didSet {
      layoutSubviews()
    }
  }

  var font: UIFont = UIFont.systemFont(ofSize: 12) {
    didSet {
      layoutSubviews()
    }
  }

  fileprivate let dismissView: UIView
  fileprivate let icon: UIImageView
  fileprivate let textLabel: UILabel

  public override init(frame: CGRect) {
    dismissView = UIView()
    icon = UIImageView()
    textLabel = UILabel()

    super.init(frame: frame)

    isUserInteractionEnabled = true

    addSubview(textLabel)
    addSubview(icon)
    addSubview(dismissView)

    dismissView.isUserInteractionEnabled = true
    textLabel.isUserInteractionEnabled = true

    dismissView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(TagView.iconTapped)))
    textLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(TagView.labelTapped)))

    backgroundColor = UIColor(white: 0.0, alpha: 0.6)
    tintColor = UIColor.white
  }

  public required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  public init(text: String) {
    dismissView = UIView()
    icon = UIImageView()
    textLabel = UILabel()

    super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))

    isUserInteractionEnabled = true

    addSubview(textLabel)
    addSubview(icon)
    addSubview(dismissView)

    dismissView.isUserInteractionEnabled = true
    textLabel.isUserInteractionEnabled = true

    dismissView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(TagView.iconTapped)))
    textLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(TagView.labelTapped)))

    self.text = text

    backgroundColor = UIColor(white: 0.0, alpha: 0.6)
    tintColor = UIColor.white
  }

  override func layoutSubviews() {
    icon.frame = CGRect(x: marginLeft, y: marginTop + 4, width: 8, height: 8)
    icon.image = iconImage?.withRenderingMode(.alwaysTemplate)
    icon.tintColor = tintColor

    let textLeft: Int

    if icon.image != nil {
      dismissView.isUserInteractionEnabled = true
      textLeft = marginLeft + Int(icon.frame.width ) + marginLeft / 2
    } else {
      dismissView.isUserInteractionEnabled = false
      textLeft = marginLeft
    }

    textLabel.frame = CGRect(x: textLeft, y: marginTop, width: 100, height: 20)
    textLabel.backgroundColor = UIColor(white: 0, alpha: 0.0)
    if maxLength > 0 && text.count > maxLength {
      textLabel.text = text.prefix(maxLength)+"..."
    }else{
      textLabel.text = text
    }
    textLabel.textAlignment = .center
    textLabel.font = font
    textLabel.textColor = tintColor
    textLabel.sizeToFit()

    let tagHeight = Int(max(textLabel.frame.height,14)) + marginTop * 2
    let tagWidth = textLeft + Int(max(textLabel.frame.width,14)) + marginLeft

    let dismissLeft = Int(icon.frame.origin.x) + Int(icon.frame.width) + marginLeft / 2
    dismissView.frame = CGRect(x: 0, y: 0, width: dismissLeft, height: tagHeight)

    frame = CGRect(x: Int(frame.origin.x), y: Int(frame.origin.y), width: tagWidth, height: tagHeight)
    layer.cornerRadius = bounds.height / 2
  }

  // MARK: Actions

  @objc func iconTapped(){
    delegate?.tagDismissed?(self)
  }

  @objc func labelTapped(){
    delegate?.tagTouched?(self)
  }

}

// MARK: TagViewDelegate
@objc protocol TagViewDelegate {

  @objc optional func tagTouched(_ tag: TagView)

  @objc optional func tagDismissed(_ tag: TagView)

}

extension CloudTagView: TagViewDelegate {

  public func tagDismissed(_ tag: TagView) {
    delegate?.tagDismissed?(tag)

    if removeOnDismiss {
      if let index = tags.firstIndex(of: tag) {
        tags.remove(at: index)
      }
    }
  }

  public func tagTouched(_ tag: TagView) {
    delegate?.tagTouched?(tag)
  }

}


let viewController = ViewController()
PlaygroundPage.current.liveView = viewController
PlaygroundPage.current.needsIndefiniteExecution = true

1 Ответ

0 голосов
/ 08 января 2020

UIStackView может выстраивать подпредставления в ряд для вас, в том числе с конечным выравниванием. Вот пример детской площадки:

import SwiftUI
import PlaygroundSupport

class V: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    let tags = ["test", "testing", "test more"].map { word -> UIView in
      let label = UILabel()
      label.text = word
      label.translatesAutoresizingMaskIntoConstraints = false
      let background = UIView()
      background.backgroundColor = .cyan
      background.layer.cornerRadius = 8
      background.clipsToBounds = true
      background.addSubview(label)
      NSLayoutConstraint.activate([
        background.centerXAnchor.constraint(equalTo: label.centerXAnchor),
        background.centerYAnchor.constraint(equalTo: label.centerYAnchor),
        background.widthAnchor.constraint(equalTo: label.widthAnchor, constant: 16),
        background.heightAnchor.constraint(equalTo: label.heightAnchor, constant: 16),
      ])
      return background
    }
    let stack = UIStackView.init(arrangedSubviews: [UIView()] + tags)
    stack.translatesAutoresizingMaskIntoConstraints = false
    stack.axis = .horizontal
    stack.alignment = .trailing
    stack.spacing = 12
    view.addSubview(stack)
    NSLayoutConstraint.activate([
      stack.topAnchor.constraint(equalTo: view.topAnchor),
      stack.widthAnchor.constraint(equalTo: view.widthAnchor),
    ])
    view.backgroundColor = .white
  }
}
PlaygroundPage.current.liveView = V()
...