水平UIPickerView单元格尺寸设置及内容适配问题求助
解决水平UIPickerView在50x50尺寸下的单元格文本适配问题
你遇到的核心问题是UIPickerView旋转后坐标系的映射关系没理清,加上自定义单元格的文本没有做自适应处理,导致50x50尺寸下长文本显示异常。下面是具体的修复方案:
关键调整点
- 修正PickerView的frame与旋转逻辑:旋转-90度后,PickerView的宽高会互换,需要重新设置原始frame并调整位置,确保最终显示为50x50的水平样式。
- 适配单元格的尺寸与文本显示:给自定义label开启自动缩小字体或换行功能,让长文本能在有限空间内完整展示;同时修正
rowHeightForComponent和widthForComponent的返回值,匹配旋转后的实际显示尺寸。 - 移除硬编码布局:用AutoLayout替代固定frame,避免布局冲突,提升视图复用性。
修复后的完整代码
import UIKit class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource { var data = ["Hello", "WorldWorld", "Of", "PickerPickerPicker", "View"] let viewPicker = UIPickerView() let pickerTargetSize: CGFloat = 50 // 目标显示的宽高 let labelFont = UIFont(name:"verdana", size: 24.0)! override func viewDidLoad() { super.viewDidLoad() viewPicker.delegate = self viewPicker.dataSource = self viewPicker.layer.borderColor = UIColor.black.cgColor viewPicker.layer.borderWidth = 1.5 // 旋转PickerView为水平方向 let rotationAngle = -90 * (.pi / 180) viewPicker.transform = CGAffineTransform(rotationAngle: rotationAngle) // 旋转后宽高互换,所以原始frame宽设为目标尺寸,高设为父视图宽度(容纳所有选项) viewPicker.frame = CGRect(x: 0, y: 0, width: pickerTargetSize, height: view.frame.width) // 调整center,让PickerView在屏幕中央水平展示 viewPicker.center = CGPoint(x: view.center.x, y: view.center.y + (view.frame.width - pickerTargetSize)/2) self.view.addSubview(viewPicker) } func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return data.count } // 旋转后,rowHeight对应PickerView的显示宽度(即我们要的50) func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { return pickerTargetSize } // 旋转后,widthForComponent对应每个选项的高度,可动态计算最长文本高度确保显示完整 func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat { let maxTextHeight = data.map { $0.heightOfString(usingFont: labelFont) }.max() ?? pickerTargetSize return max(maxTextHeight, pickerTargetSize) } func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { let cellView = view as? UIView ?? UIView() cellView.backgroundColor = .clear // 复用label,避免重复创建 let label: UILabel if let existingLabel = cellView.viewWithTag(100) as? UILabel { label = existingLabel } else { label = UILabel() label.tag = 100 label.textAlignment = .center label.font = labelFont // 开启自动缩小字体,适配长文本 label.adjustsFontSizeToFitWidth = true label.minimumScaleFactor = 0.5 // 可选:允许换行,需打开下面注释 // label.numberOfLines = 0 cellView.addSubview(label) // 用AutoLayout约束label,替代硬编码frame label.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ label.centerXAnchor.constraint(equalTo: cellView.centerXAnchor), label.centerYAnchor.constraint(equalTo: cellView.centerYAnchor), label.widthAnchor.constraint(equalTo: cellView.widthAnchor, multiplier: 0.9), label.heightAnchor.constraint(equalTo: cellView.heightAnchor, multiplier: 0.9) ]) } label.text = data[row] // 旋转label抵消PickerView的旋转,让文本正向显示 label.transform = CGAffineTransform(rotationAngle: 90 * (.pi / 180)) return cellView } } extension String { func widthOfString(usingFont font: UIFont) -> CGFloat { let fontAttributes = [NSAttributedString.Key.font: font] let size = self.size(withAttributes: fontAttributes) return size.width } func heightOfString(usingFont font: UIFont) -> CGFloat { let fontAttributes = [NSAttributedString.Key.font: font] let size = self.size(withAttributes: fontAttributes) return size.height } }
核心逻辑说明
- PickerView布局:旋转后将原始frame的宽设为50,高设为父视图宽度,确保横向有足够空间容纳所有选项,再通过
center调整到屏幕中央。 - 文本适配:开启
adjustsFontSizeToFitWidth让长文本自动缩小字体,也可以选择开启换行(设置numberOfLines=0),两种方式都能解决文本截断问题。 - 视图复用:复用已有的cell和label,提升性能;用AutoLayout替代硬编码frame,避免旋转后出现布局错位。
内容的提问来源于stack exchange,提问作者goga
相关产品推荐
相关产品推荐

