首页 > 解决方案 > 如何通过自动布局获取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方法获得宽度,但没有运气

标签: iosswiftxcodeuitableviewuser-interface

解决方案


有多种方法可以解决这个问题。

一种选择...不是仅将红色虚线绘制为形状层,而是将灰色线、红色虚线和绿色条绘制为 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这样你就可以看到绿色条动态增长。


推荐阅读