ios - 如何通过自动布局获取iOS中2个元素之间的动态距离
问题描述
我有以下需要绘制虚线的 UI。我不确定如何计算所需的宽度,尤其是在自动布局动态确定屏幕间距的情况下。绿条是单一的UITableViewCells
。下面是绘制虚线的代码:
extension UIView {
func addDashedBorder(barWidth: Int, maxWidth: Int) {
//Create a CAShapeLayer
let shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 2
// passing an array with the values [2,3] sets a dash pattern that alternates between a 2-user-space-unit-long painted segment and a 3-user-space-unit-long unpainted segment
shapeLayer.lineDashPattern = [2,3]
let path = CGMutablePath()
path.addLines(between: [CGPoint(x: 0, y: 0),
CGPoint(x: barWidth - maxWidth, y: 0)])
shapeLayer.path = path
layer.addSublayer(shapeLayer)
}
}
我需要红线与灰线重叠。鉴于调用委托函数的时间导致灰线的宽度不一致,我该怎么做?我目前正在尝试通过该didMoveToSuperview
方法获得宽度,但没有运气
解决方案
有多种方法可以解决这个问题。
一种选择...不是仅将红色虚线绘制为形状层,而是将灰色线、红色虚线和绿色条绘制为 3 个子层。
- 底层:灰线
- 中间层:红色虚线
- 顶层:绿色“条”线
由于绿条将覆盖红点和灰线,它们都可以跨越单元格左边缘和第一个标签左边缘之间的整个宽度。这意味着唯一需要改变大小的是绿色条层。
这是一些示例代码:
class MyProgressBarView: UIView {
var progress: CGFloat = 0.0 {
didSet {
setNeedsLayout()
}
}
let greenBar = CAShapeLayer()
let grayBar = CAShapeLayer()
let redBar = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
layer.addSublayer(grayBar)
layer.addSublayer(redBar)
layer.addSublayer(greenBar)
redBar.strokeColor = UIColor.red.cgColor
redBar.lineWidth = 2
// passing an array with the values [2,3] sets a dash pattern that alternates between a 2-user-space-unit-long painted segment and a 3-user-space-unit-long unpainted segment
redBar.lineDashPattern = [2,3]
grayBar.strokeColor = UIColor.lightGray.cgColor
grayBar.lineWidth = 1
greenBar.strokeColor = UIColor.green.cgColor
}
override func layoutSubviews() {
super.layoutSubviews()
var path = CGMutablePath()
path.addLines(between: [CGPoint(x: 0, y: bounds.height * 0.5),
CGPoint(x: bounds.width, y: bounds.height * 0.5)])
grayBar.path = path
redBar.path = path
path = CGMutablePath()
// cell height may change, so set greenBar's height here
greenBar.lineWidth = bounds.height
path.addLines(between: [CGPoint(x: 0, y: bounds.height * 0.5),
CGPoint(x: bounds.width * progress, y: bounds.height * 0.5)])
greenBar.path = path
}
}
class ProgressCell: UITableViewCell {
@IBOutlet var val1Label: UILabel!
@IBOutlet var val2Label: UILabel!
@IBOutlet var progView: MyProgressBarView!
var val1: CGFloat = 0.0 {
didSet {
val1Label.text = "\(val1)"
// make sure we don't divide by Zero
progView.progress = val2 > 0 ? val1 / val2 : 0.0
}
}
var val2: CGFloat = 0.0 {
didSet {
val2Label.text = "$\(val2)M"
// make sure we don't divide by Zero
progView.progress = val2 > 0 ? val1 / val2 : 0.0
}
}
}
struct MyValues {
var v1: CGFloat = 0.0
var v2: CGFloat = 0.0
}
class ProgressTableViewController: UITableViewController {
var myData: [MyValues] = [
MyValues(v1: 50.0, v2: 250.0),
MyValues(v1: 80.0, v2: 250.0),
MyValues(v1: 105.0, v2: 250.0),
MyValues(v1: 127.0, v2: 250.0),
MyValues(v1: 93.0, v2: 250.0),
MyValues(v1: 80.0, v2: 250.0),
MyValues(v1: 205.0, v2: 250.0),
MyValues(v1: 177.0, v2: 250.0),
MyValues(v1: 245.0, v2: 250.0),
]
override func viewDidLoad() {
super.viewDidLoad()
}
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: "ProgressCell", for: indexPath) as! ProgressCell
let d = myData[indexPath.row]
cell.val1 = d.v1
cell.val2 = d.v2
cell.selectionStyle = .none
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let c = tableView.cellForRow(at: indexPath) as? ProgressCell {
let d = myData[indexPath.row]
var v = d.v1 + 10.0
v = min(v, d.v2)
myData[indexPath.row].v1 = v
c.val1 = v
}
}
}
这是故事板源:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="jKi-p9-QPh">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Progress Table View Controller-->
<scene sceneID="XK3-bb-mGO">
<objects>
<tableViewController id="xPK-h1-D8d" customClass="ProgressTableViewController" customModule="scratchy" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="Ofl-mQ-aYH">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="ProgressCell" id="hIa-mP-rya" customClass="ProgressCell" customModule="scratchy" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hIa-mP-rya" id="fVi-sL-ouy">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1va-uW-uvw" customClass="MyProgressBarView" customModule="scratchy" customModuleProvider="target">
<rect key="frame" x="16" y="11" width="187" height="21.5"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="150" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UNh-No-HjD">
<rect key="frame" x="211" y="11" width="50" height="21.5"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="ziy-02-rwL"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="$30.45M" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aDM-ad-OT7">
<rect key="frame" x="269" y="11" width="90" height="21.5"/>
<constraints>
<constraint firstAttribute="width" constant="90" id="cMB-Uy-RTf"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="1va-uW-uvw" firstAttribute="leading" secondItem="fVi-sL-ouy" secondAttribute="leadingMargin" id="BVh-vs-fur"/>
<constraint firstItem="UNh-No-HjD" firstAttribute="leading" secondItem="1va-uW-uvw" secondAttribute="trailing" constant="8" id="Fh8-Nf-sU3"/>
<constraint firstAttribute="trailingMargin" secondItem="aDM-ad-OT7" secondAttribute="trailing" id="GC9-ua-CdP"/>
<constraint firstItem="1va-uW-uvw" firstAttribute="top" secondItem="fVi-sL-ouy" secondAttribute="topMargin" id="Mg5-6e-QBM"/>
<constraint firstItem="aDM-ad-OT7" firstAttribute="top" secondItem="fVi-sL-ouy" secondAttribute="topMargin" id="YCc-VG-5rv"/>
<constraint firstAttribute="bottomMargin" secondItem="UNh-No-HjD" secondAttribute="bottom" id="bJG-Mk-j6m"/>
<constraint firstItem="UNh-No-HjD" firstAttribute="top" secondItem="fVi-sL-ouy" secondAttribute="topMargin" id="eKv-UH-Opf"/>
<constraint firstItem="aDM-ad-OT7" firstAttribute="leading" secondItem="UNh-No-HjD" secondAttribute="trailing" constant="8" id="laQ-5R-RZa"/>
<constraint firstAttribute="bottomMargin" secondItem="1va-uW-uvw" secondAttribute="bottom" id="loG-KQ-7xC"/>
<constraint firstAttribute="bottomMargin" secondItem="aDM-ad-OT7" secondAttribute="bottom" id="wml-fK-ewt"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="progView" destination="1va-uW-uvw" id="Ips-T7-vZ5"/>
<outlet property="val1Label" destination="UNh-No-HjD" id="Gqe-bP-u0c"/>
<outlet property="val2Label" destination="aDM-ad-OT7" id="gMP-5N-FIb"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="xPK-h1-D8d" id="iaK-Nh-quP"/>
<outlet property="delegate" destination="xPK-h1-D8d" id="1qh-e9-C6l"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="Gie-Xj-DLT"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="9As-Tc-u8W" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="357.60000000000002" y="2048.7256371814096"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="k92-RW-oNV">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="jKi-p9-QPh" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="HPb-es-8az">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="xPK-h1-D8d" kind="relationship" relationship="rootViewController" id="QXa-oP-Nwz"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vwe-Vd-YNO" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-581.60000000000002" y="2048.7256371814096"/>
</scene>
</scenes>
</document>
结果:
并且,随着设备旋转,您可以看到它自动处理尺寸变化 - 没有特殊代码:
如果你把它添加到一个新项目并运行它,我也实现didSelectRowAt
了——每次点击一行时,它都会增加“左值”,10.0
这样你就可以看到绿色条动态增长。
推荐阅读
- ios - 在 Swift 5 中更改 Lottie AnimationView 的大小
- mysql - 从以下位置插入/转换日期:C++ 到 mySQL db
- regex - 正则表达式匹配目录路径
- javascript - 如何将一串字母和数字拆分为数组并在javascript中分别对它们进行排序
- javascript - Jasmine - undefined 不是构造函数
- javascript - React redux,动作有效负载在 try/catch 块中看不到变量
- pytorch - nmt 中注意力解码器的余弦相似度
- functional-programming - 如何在球拍中编写排除功能?
- javascript - Google Scripts - 重新替换、转置和拆分数据
- reactjs - 我得到一个 Failed to restore the NuGet packages with react native run-windows