collectionview 或 uitableview 内的 Stackview
Posted
技术标签:
【中文标题】collectionview 或 uitableview 内的 Stackview【英文标题】:Stackview inside collectionview or uitableview 【发布时间】:2020-11-08 13:53:59 【问题描述】:我正在尝试实现文本的布局,然后是图像(基于纵横比计算的图像高度),然后是文本等等。问题是我将视图添加到随机压缩视图的堆栈视图有时图像视图会在文本中消失一段时间,它没有一致的行为。
我在 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
如果可能的话,我需要一些关于这个问题的指导。
谢谢你的帮助
【问题讨论】:
欢迎来到 ***。请使用tour 并查看How to Ask。您需要提供有关您正在做什么的更多信息......这是垂直或水平堆栈视图吗?你是如何设置约束的?您是否将您的单元设计为故事板原型?如果是这样,请显示该布局。如果您是通过代码执行此操作,请显示您的代码。 @DonMag 感谢您的回复,您可以看到这是我第一次在 *** 上提问。现在回答你的问题,它是一个垂直堆栈视图,现在约束只是根据我的纵横比计算为我的图像视图设置的高度约束,并且标签设置为 numberOfLines 为 0。现在以编程方式创建视图,我在帖子中附上了代码作为参考点。非常感谢。 @DonMag 我自己使用了一个滚动视图,里面有这个stackview,它显示得非常好,当我把stackview放在uitableviewcell或uicollectionviewcell中时会发生这个问题。 您的代码中有很多是在其他地方定义的(所以我们不知道它是什么),而且您现在有很多事情要做。我建议从更简单的开始...创建一个带有垂直堆栈视图的单元格,其中包含几个元素(例如UILabel
和UIImageView
)并让它工作。然后开始添加您的其他元素并将代码拆分为扩展。事实上,太多的未知数。
@DonMag 太好了,所以我将从一个简单的 stackview 开始,它有一个 text 和 imageview 并从那里开始。谢谢,会及时更新。希望一切顺利,谢谢。
【参考方案1】:
这是一个(相当)基本的例子。
我们将使用这样的数据结构:
struct VarDevStruct
var first: String = ""
var second: String = ""
var imageName: String = ""
cell 类有一个垂直堆栈视图,其中包含:
多行标签 水平堆栈视图 具有 80x80 图像视图和标签 多行标签如果数据结构中的任何元素是空字符串,我们会将单元格中的相应元素设置为隐藏。
首先,结果:
向下滚动到包含不同数据的几行后:
并旋转:
这是完整的代码...里面有很多cmets,所以应该清楚代码在做什么。
数据结构
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
【讨论】:
太好了,我开始工作了,一切都很好,非常感谢。我正在做的是添加标签,然后当我有图像时,我在内部附加了一个带有图像视图的 UIView,在从网络调用加载图像后,我更新了该视图的约束(问题就在这里,这就是它的原因挤压其他标签),非常感谢您的帮助,非常感谢。以上是关于collectionview 或 uitableview 内的 Stackview的主要内容,如果未能解决你的问题,请参考以下文章
UICollectionView insertItemsAtIndexPaths:似乎重新加载collectionview或自动滚动到顶部
如何在 CollectionView 中显示从照片库或相机中选择的图像?画廊视图
在单元格标签等于给定字符串的collectionView或TableView上选择行/部分?
如何在巨大的 CollectionView 中使用 SDWebImage 或 AFNetworking 在磁盘上缓存图像?