ios - 集合视图或 uitableview 内的 Stackview
问题描述
我正在尝试实现文本的布局,然后是图像(基于纵横比计算的图像高度),然后是文本等等。问题是我将视图添加到随机压缩视图中的堆栈视图有时图像视图会在文本中消失一段时间,它没有一致的行为。
我在 uitableview 和 uicolletion 视图上都试过了,结果是一样的。是否将上述观点的组合视为此类用例的最佳实践?如果不是这样,最好的做法是什么?
class MyStackyView: UIStackView {
// Main variables
weak var videoPlayerDelegate: AVPlayerViewDelegate?
private var avVideoPlayersVC: [AVPlayerViewController] = []
var content: [Content]! {
didSet {
contentCombined = Utility.shared.combineToNew(contents: content)
}
}
private var contentCombined: [Content] = [] {
didSet {
populatePostContent()
}
}
var contentViews: [UIView] = [] // Holds the views created
override init(frame: CGRect) {
super.init(frame: frame)
configureView()
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
print("DiaryPostView:: Deinitalized")
}
private func configureView() {
axis = .vertical
distribution = .fill
alignment = .fill
spacing = 0
}
}
// 扩展以填充帖子内容扩展 MyStackyView {
private func populatePostContent() {
for content in contentCombined {
if content.isMedia {
addMedia(content)
} else {
addText(content.text)
}
}
}
}
// 扩展添加所需的视图扩展 MyStackyView {
private func addText(_ text: String?, place: MediaPlace = .center) {
let textView = generateDefaultTextView()
//let parsedText = HTMLParser.shared.parseHTMLToAttributed(string: text ?? "") // fix font issue
switch place {
case .center:
append(textView)
contentViews.append(textView)
}
textView.text = text
// لما استخدم ال parsedtext مرة النص بطلع مع الfont و مرة لا
}
private func addMedia(_ content: Content) {
let avPlayerVC = getAVPlayerViewController()
let mediaView = generateDefaultMediaView()
switch content.getRawPlace() {
case .center:
append(mediaView)
contentViews.append(mediaView)
addText(content.text)
NetworkManager().downloadMedia(content.img!, into: mediaView, avPlayerViewController: avPlayerVC) {
}
}
}
}
扩展 MyStackyView {
private func generateDefaultTextView() -> UILabel {
let textView = UILabel()
textView.backgroundColor = .clear
textView.numberOfLines = 0
textView.font = UIFont.customFont(.openSans, .regular, .title1, 17)
return textView
}
private func generateDefaultHorizontalStack() -> UIStackView {
let horizontalStack = UIStackView()
horizontalStack.axis = .horizontal
horizontalStack.distribution = .fill
horizontalStack.alignment = .fill
return horizontalStack
}
private func generateDefaultMediaView() -> MediaSliderView {
let mediaSliderView = MediaSliderView()
return mediaSliderView
}
private func getAVPlayerViewController() -> AVPlayerViewController? {
videoPlayerDelegate?.getAVPlayerVC?()
}
func deallocateAVPlayers() {
for player in avVideoPlayersVC {
player.removeFromParent()
}
avVideoPlayersVC.removeAll()
}
}
我在我的 uitableviewcell 中初始化类的一个变量,然后添加这些约束
contentView.addSubview(MyStackyView)
MyStackyView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8).isActive = true
MyStackyView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8).isActive = true
MyStackyView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16).isActive = true
MyStackyView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16).isActive = true
如果可能的话,我需要一些关于这个问题的指导。
谢谢,感谢帮助
解决方案
这是一个(相当)基本的例子。
我们将使用这样的数据结构:
struct VarDevStruct {
var first: String = ""
var second: String = ""
var imageName: String = ""
}
单元类有一个垂直堆栈视图,其中包含:
- 多行标签
- 水平堆栈视图
- 具有 80x80 图像视图和标签
- 多行标签
如果数据结构中的任何元素是空字符串,我们会将单元格中的相应元素设置为hidden。
首先,结果:
向下滚动到具有不同数据的几行后:
并旋转:
这是完整的代码……里面有很多注释,所以应该清楚代码在做什么。
数据结构
struct VarDevStruct {
var first: String = ""
var second: String = ""
var imageName: String = ""
}
细胞类
class VarDevCell: UITableViewCell {
let firstLabel = UILabel()
let secondLabel = UILabel()
let imgView = UIImageView()
let imgNameLabel = UILabel()
let vStack = UIStackView()
let hStack = UIStackView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
// stack view properties
vStack.axis = .vertical
vStack.alignment = .fill
vStack.distribution = .fill
vStack.spacing = 8
vStack.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(vStack)
// let's use the default cell margins
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
// constrain stack view to all 4 sides
vStack.topAnchor.constraint(equalTo: g.topAnchor),
vStack.leadingAnchor.constraint(equalTo: g.leadingAnchor),
vStack.trailingAnchor.constraint(equalTo: g.trailingAnchor),
vStack.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
// subview properties
// background colors to make it easy to see the frames
firstLabel.backgroundColor = .yellow
secondLabel.backgroundColor = .green
imgView.backgroundColor = .red
imgNameLabel.backgroundColor = .cyan
// multi-line labels
firstLabel.numberOfLines = 0
secondLabel.numberOfLines = 0
imgNameLabel.textAlignment = .center
// image view defaults to scaleToFill
// let's set it to scaleAspectFit
imgView.contentMode = .scaleAspectFit
// horizontal stack view
hStack.axis = .horizontal
hStack.alignment = .center
hStack.distribution = .fill
hStack.spacing = 8
// add subviews to horizontal stack view
hStack.addArrangedSubview(imgView)
hStack.addArrangedSubview(imgNameLabel)
// let's fill the vertical stack view with
// label
// hStack with 80x80 imageview and label with image name
// label
vStack.addArrangedSubview(firstLabel)
vStack.addArrangedSubview(hStack)
vStack.addArrangedSubview(secondLabel)
// set image view width and height
imgView.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1.0).isActive = true
}
func fillData(_ vdStruct: VarDevStruct) -> Void {
firstLabel.text = vdStruct.first
secondLabel.text = vdStruct.second
imgNameLabel.text = vdStruct.imageName
// does our data have an image name?
if !vdStruct.imageName.isEmpty {
if #available(iOS 13.0, *) {
if let img = UIImage(systemName: vdStruct.imageName) {
imgView.image = img
}
} else {
// Fallback on earlier versions
if let img = UIImage(named: vdStruct.imageName) {
imgView.image = img
}
}
}
// hide elements that we don't need in this cell
firstLabel.isHidden = vdStruct.first.isEmpty
secondLabel.isHidden = vdStruct.second.isEmpty
hStack.isHidden = vdStruct.imageName.isEmpty
}
}
控制器类
class VarDevTableViewController: UITableViewController {
var myData: [VarDevStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
// register cell class for reuse
tableView.register(VarDevCell.self, forCellReuseIdentifier: "cell")
// generate some sample data
myData = makeSampleData()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: nil, completion: {
_ in
// make sure table re-calculates row heights
UIView.setAnimationsEnabled(false)
self.tableView.performBatchUpdates(nil, completion: nil)
UIView.setAnimationsEnabled(true)
})
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! VarDevCell
cell.fillData(myData[indexPath.row])
return cell
}
func makeSampleData() -> [VarDevStruct] {
var a: [VarDevStruct] = []
// 15 sample data elements
for i in 1...15 {
let d = VarDevStruct(first: "This is the text for the first label in row: \(i).",
second: "This will be a longer string to be used as the text for the second label in row \(i) (long enough to make sure we're getting some word wrapping).",
imageName: "\(i).square.fill")
a.append(d)
}
// change some of the sample data for variations
// (arrays are zero-based)
// fifth row: no first label
a[4].first = ""
a[4].second = "This row has no First label text."
// sixth row: no image
a[5].first = "This row has no image."
a[5].imageName = ""
// seventh row: no second label
a[6].first = "This row has no second label."
a[6].second = ""
// eigth row: no image or second label
a[7].first = "This row has no image, and has no second label. The next row (9) has image only."
a[7].imageName = ""
a[7].second = ""
// ninth row: image only
a[8].first = ""
a[8].second = ""
// tenth row: first label with mutliple lines
a[9].first = "One\nTwo\nThree\nFour"
a[9].second = "This row has embedded newline chars in the text of the first label."
return a
}
}