自定义 UITableViewCell、UISlider 子视图意外的大小调整行为

问题描述 投票:0回答:1

我在自定义 UITableViewCell 中有一个 UISlider。 当我查看

awakeFromNib
中滑块的大小时,
.frame
属性显示的是在故事板中设置的滑块大小,而不是视图出现时绘制的最终大小。

我原以为所有设置都是在

awakeFromNib
中完成的,但滑块的大小似乎在 awakeFromNib 和其最终外观之间发生了变化。

我在 2015 年发现了一个类似的问题,该问题已发布答案,但实际上并未得到解决。

UITableViewCell:了解生命周期

我在2016年也发现了一个类似的问题,但这个问题似乎不适用于我的情况。

Swift UITableViewCell 子视图布局更新延迟

我添加了故事板中设置的约束的屏幕截图。

swift uitableview uikit awakefromnib
1个回答
0
投票

我们不知道单元格(及其 UI 组件)的大小,直到

layoutSubviews()

因此,假设您将箭头位置设置为百分比,请按照以下方式在单元类中实现

layoutSubviews()

override func layoutSubviews() {
    super.layoutSubviews()
    
    // the thumb "circle" extends to the bounds / frame of the slider
    // so, this is how we get the
    //  thumb center-to-center
    //  when value is 0 or 1.0
    let trackRect = theSlider.trackRect(forBounds: theSlider.bounds)
    let thumbRect = theSlider.thumbRect(forBounds: theSlider.bounds, trackRect: trackRect, value: 0.0)
    let rangeWidth = theSlider.bounds.width - thumbRect.width
    
    // Zero will be 1/2 of the width of the thumbRect
    //  minus 2 (because the thumb image is slightly offset from the thumb rect)
    let xOffset = (thumbRect.width * 0.5) - 2.0
    
    // create the arrow constraints if needed
    if startConstraint == nil {
        startConstraint = startArrow.centerXAnchor.constraint(equalTo: theSlider.leadingAnchor)
        startConstraint.isActive = true
    }
    if endConstraint == nil {
        endConstraint = endArrow.centerXAnchor.constraint(equalTo: theSlider.leadingAnchor)
        endConstraint.isActive = true
    }
    
    // set arrow constraint constants
    startConstraint.constant = rangeWidth * startTime + xOffset
    endConstraint.constant = rangeWidth * endTime + xOffset
}

我假设所有行的滑块都有相同的“时间范围”,因此我们可以得到类似的结果(我将拇指色调设置为半透明,箭头 y 位置设置为这样我们可以看到对齐情况):

对于完整的示例(生成该输出),请使用此故事板:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="J6Q-B3-lSE">
    <device id="retina6_12" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22130"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Slider TableVC-->
        <scene sceneID="yXs-sI-IdP">
            <objects>
                <tableViewController id="J6Q-B3-lSE" customClass="SliderTableVC" customModule="TmpTmp" customModuleProvider="target" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" id="JRC-Ch-MFI">
                        <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                        <prototypes>
                            <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="sliderCell" rowHeight="233" id="SCF-ak-VtA" customClass="SliderCell" customModule="TmpTmp" customModuleProvider="target">
                                <rect key="frame" x="0.0" y="50" width="393" height="233"/>
                                <autoresizingMask key="autoresizingMask"/>
                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="SCF-ak-VtA" id="OEa-iz-t0R">
                                    <rect key="frame" x="0.0" y="0.0" width="393" height="233"/>
                                    <autoresizingMask key="autoresizingMask"/>
                                    <subviews>
                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="55g-fd-LTL">
                                            <rect key="frame" x="20" y="11.000000000000002" width="47" height="21.666666666666671"/>
                                            <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
                                            <color key="textColor" red="0.016804177310000001" green="0.19835099580000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="m8D-IM-8fn">
                                            <rect key="frame" x="327" y="11" width="46" height="23"/>
                                            <fontDescription key="fontDescription" type="system" pointSize="19"/>
                                            <nil key="textColor"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iHQ-9O-6Vz">
                                            <rect key="frame" x="20" y="33.666666666666664" width="65" height="28.666666666666664"/>
                                            <string key="text">Label
Label Label</string>
                                            <fontDescription key="fontDescription" type="system" pointSize="12"/>
                                            <nil key="textColor"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="32b-om-4Ux">
                                            <rect key="frame" x="178" y="41" width="37.333333333333343" height="18"/>
                                            <fontDescription key="fontDescription" type="system" pointSize="15"/>
                                            <color key="textColor" systemColor="systemRedColor"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="right" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PaO-BC-b0I">
                                            <rect key="frame" x="308" y="35" width="65" height="28.666666666666671"/>
                                            <string key="text">Label
Label Label</string>
                                            <fontDescription key="fontDescription" type="system" pointSize="12"/>
                                            <nil key="textColor"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="1000" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="r3Y-fY-1x1">
                                            <rect key="frame" x="20" y="114" width="26.333333333333329" height="12"/>
                                            <fontDescription key="fontDescription" type="system" pointSize="10"/>
                                            <nil key="textColor"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="1000" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="B0s-gC-ToH">
                                            <rect key="frame" x="346.66666666666669" y="114" width="26.333333333333314" height="12"/>
                                            <fontDescription key="fontDescription" type="system" pointSize="10"/>
                                            <nil key="textColor"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                        <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="5Oa-0J-dF1">
                                            <rect key="frame" x="18" y="75" width="357" height="31"/>
                                            <color key="thumbTintColor" red="0.0" green="0.97680455450000003" blue="0.0" alpha="0.25" colorSpace="custom" customColorSpace="sRGB"/>
                                        </slider>
                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrowshape.up" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="bGx-Yf-Gjd">
                                            <rect key="frame" x="139.33333333333334" y="93.333333333333329" width="20" height="19.666666666666671"/>
                                            <color key="tintColor" systemColor="systemRedColor"/>
                                            <constraints>
                                                <constraint firstAttribute="width" constant="20" id="Bo0-t0-Url"/>
                                                <constraint firstAttribute="width" secondItem="bGx-Yf-Gjd" secondAttribute="height" multiplier="1:1" id="Y0i-jC-1lI"/>
                                            </constraints>
                                        </imageView>
                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrowshape.up" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="1VL-rV-Hrb">
                                            <rect key="frame" x="269.66666666666669" y="93.333333333333329" width="20" height="19.666666666666671"/>
                                            <color key="tintColor" systemColor="systemRedColor"/>
                                            <constraints>
                                                <constraint firstAttribute="width" secondItem="1VL-rV-Hrb" secondAttribute="height" multiplier="1:1" id="Sqy-RN-XxK"/>
                                            </constraints>
                                        </imageView>
                                    </subviews>
                                    <constraints>
                                        <constraint firstItem="5Oa-0J-dF1" firstAttribute="leading" secondItem="55g-fd-LTL" secondAttribute="leading" id="37d-l5-EIJ"/>
                                        <constraint firstItem="r3Y-fY-1x1" firstAttribute="top" secondItem="bGx-Yf-Gjd" secondAttribute="bottom" constant="1" id="4FI-YR-2Wo"/>
                                        <constraint firstItem="5Oa-0J-dF1" firstAttribute="top" secondItem="32b-om-4Ux" secondAttribute="bottom" constant="16" id="4HV-Pg-Nh8"/>
                                        <constraint firstItem="B0s-gC-ToH" firstAttribute="top" secondItem="r3Y-fY-1x1" secondAttribute="top" id="7mH-Fq-0GS"/>
                                        <constraint firstItem="1VL-rV-Hrb" firstAttribute="centerX" secondItem="5Oa-0J-dF1" secondAttribute="trailing" multiplier="0.75" priority="250" id="AOS-Da-kaA"/>
                                        <constraint firstAttribute="bottomMargin" relation="greaterThanOrEqual" secondItem="r3Y-fY-1x1" secondAttribute="bottom" priority="999" id="DmE-Is-Qe0"/>
                                        <constraint firstItem="55g-fd-LTL" firstAttribute="top" secondItem="OEa-iz-t0R" secondAttribute="topMargin" id="HRM-zZ-7BL"/>
                                        <constraint firstItem="1VL-rV-Hrb" firstAttribute="width" secondItem="bGx-Yf-Gjd" secondAttribute="width" id="KKN-J1-SFy"/>
                                        <constraint firstItem="PaO-BC-b0I" firstAttribute="trailing" secondItem="m8D-IM-8fn" secondAttribute="trailing" id="MHS-f4-WMg"/>
                                        <constraint firstItem="r3Y-fY-1x1" firstAttribute="leading" secondItem="OEa-iz-t0R" secondAttribute="leadingMargin" id="MXt-DF-eMa"/>
                                        <constraint firstItem="iHQ-9O-6Vz" firstAttribute="leading" secondItem="55g-fd-LTL" secondAttribute="leading" id="NnH-rl-Yoo"/>
                                        <constraint firstItem="m8D-IM-8fn" firstAttribute="top" secondItem="OEa-iz-t0R" secondAttribute="topMargin" id="OBB-ee-MN9"/>
                                        <constraint firstItem="PaO-BC-b0I" firstAttribute="top" secondItem="m8D-IM-8fn" secondAttribute="bottom" constant="1" id="Rfh-vR-JWI"/>
                                        <constraint firstItem="iHQ-9O-6Vz" firstAttribute="top" secondItem="55g-fd-LTL" secondAttribute="bottom" constant="1" id="T2v-t9-1Ii"/>
                                        <constraint firstItem="32b-om-4Ux" firstAttribute="top" secondItem="OEa-iz-t0R" secondAttribute="topMargin" constant="30" id="eLI-Ma-Pwa"/>
                                        <constraint firstItem="bGx-Yf-Gjd" firstAttribute="top" secondItem="5Oa-0J-dF1" secondAttribute="bottom" constant="-12" id="g1H-4E-EL9"/>
                                        <constraint firstAttribute="trailingMargin" secondItem="m8D-IM-8fn" secondAttribute="trailing" id="jzP-r4-iTn"/>
                                        <constraint firstItem="1VL-rV-Hrb" firstAttribute="top" secondItem="bGx-Yf-Gjd" secondAttribute="top" id="oMm-uB-GeM"/>
                                        <constraint firstItem="55g-fd-LTL" firstAttribute="leading" secondItem="OEa-iz-t0R" secondAttribute="leadingMargin" id="q1Z-Gp-ECh"/>
                                        <constraint firstItem="bGx-Yf-Gjd" firstAttribute="centerX" secondItem="5Oa-0J-dF1" secondAttribute="trailing" multiplier="0.4" priority="250" id="qSG-OI-p1g"/>
                                        <constraint firstItem="32b-om-4Ux" firstAttribute="centerX" secondItem="OEa-iz-t0R" secondAttribute="centerX" id="rKf-aR-afn"/>
                                        <constraint firstAttribute="trailingMargin" secondItem="B0s-gC-ToH" secondAttribute="trailing" id="sbX-LJ-tAM"/>
                                        <constraint firstItem="5Oa-0J-dF1" firstAttribute="trailing" secondItem="m8D-IM-8fn" secondAttribute="trailing" id="tTu-4R-rwV"/>
                                    </constraints>
                                </tableViewCellContentView>
                                <connections>
                                    <outlet property="dateLabel" destination="55g-fd-LTL" id="Tfw-j5-0F5"/>
                                    <outlet property="endArrow" destination="1VL-rV-Hrb" id="Ism-o5-Qpa"/>
                                    <outlet property="maxLabel" destination="B0s-gC-ToH" id="zzZ-p4-f1K"/>
                                    <outlet property="minLabel" destination="r3Y-fY-1x1" id="7UF-gj-hhh"/>
                                    <outlet property="startArrow" destination="bGx-Yf-Gjd" id="g2G-9T-fma"/>
                                    <outlet property="startEndLabel" destination="32b-om-4Ux" id="Hks-Mk-LJS"/>
                                    <outlet property="theSlider" destination="5Oa-0J-dF1" id="WLd-hq-5Yx"/>
                                </connections>
                            </tableViewCell>
                        </prototypes>
                        <connections>
                            <outlet property="dataSource" destination="J6Q-B3-lSE" id="Bup-OR-ep9"/>
                            <outlet property="delegate" destination="J6Q-B3-lSE" id="l97-XW-bAr"/>
                        </connections>
                    </tableView>
                    <navigationItem key="navigationItem" id="xho-8q-jym"/>
                </tableViewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="YGx-B0-5lF" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="-759.5419847328244" y="1776.7605633802818"/>
        </scene>
    </scenes>
    <resources>
        <image name="arrowshape.up" catalog="system" width="123" height="128"/>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
        <systemColor name="systemRedColor">
            <color red="1" green="0.23137254900000001" blue="0.18823529410000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        </systemColor>
    </resources>
</document>

和这段代码:

class SliderCell: UITableViewCell {
    // startTime and endTime are in Percentages
    public var startTime: Double = 0.0 { didSet { setNeedsLayout() } }
    public var endTime: Double = 0.0 { didSet { setNeedsLayout() } }
    
    @IBOutlet var startArrow: UIImageView!
    @IBOutlet var endArrow: UIImageView!

    @IBOutlet var dateLabel: UILabel!
    @IBOutlet var startEndLabel: UILabel!
    
    @IBOutlet var minLabel: UILabel!
    @IBOutlet var maxLabel: UILabel!
    
    @IBOutlet var theSlider: UISlider!
    
    private var startConstraint: NSLayoutConstraint!
    private var endConstraint: NSLayoutConstraint!
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        // the thumb "circle" extends to the bounds / frame of the slider
        // so, this is how we get the
        //  thumb center-to-center
        //  when value is 0 or 1.0
        let trackRect = theSlider.trackRect(forBounds: theSlider.bounds)
        let thumbRect = theSlider.thumbRect(forBounds: theSlider.bounds, trackRect: trackRect, value: 0.0)
        let rangeWidth = theSlider.bounds.width - thumbRect.width
        
        // Zero will be 1/2 of the width of the thumbRect
        //  minus 2 (because the thumb image is slightly offset from the thumb rect)
        let xOffset = (thumbRect.width * 0.5) - 2.0
        
        // create the arrow constraints if needed
        if startConstraint == nil {
            startConstraint = startArrow.centerXAnchor.constraint(equalTo: theSlider.leadingAnchor)
            startConstraint.isActive = true
        }
        if endConstraint == nil {
            endConstraint = endArrow.centerXAnchor.constraint(equalTo: theSlider.leadingAnchor)
            endConstraint.isActive = true
        }
        
        // set arrow constraint constants
        startConstraint.constant = rangeWidth * startTime + xOffset
        endConstraint.constant = rangeWidth * endTime + xOffset
    }
}

struct MyTimeInfo {
    var startTime: Date = Date()
    var endTime: Date = Date()
}

class SliderTableVC: UITableViewController {
    
    var myData: [MyTimeInfo] = []
    
    var minTime: Double = 0
    var maxTime: Double = 24

    var minTimeStr: String = ""
    var maxTimeStr: String = ""
    
    var timeRange: Double = 24
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // let's generate some sample data
        let starts: [Double] = [
            8, 7, 11, 10.5, 8.25, 9,
        ]
        let ends: [Double] = [
            20, 23, 19, 16.5, 21.75, 21,
        ]
        let y = 2023
        let m = 11
        var d = 1
        for (s, e) in zip(starts, ends) {
            var dateComponents = DateComponents()
            dateComponents.year = y
            dateComponents.month = m
            dateComponents.day = d
            dateComponents.hour = Int(s)
            dateComponents.minute = Int((s - Double(Int(s))) * 60.0)
            let sDate = Calendar.current.date(from: dateComponents)!
            dateComponents.hour = Int(e)
            dateComponents.minute = Int((e - Double(Int(e))) * 60.0)
            let eDate = Calendar.current.date(from: dateComponents)!
            myData.append(MyTimeInfo(startTime: sDate, endTime: eDate))
            d += 1
        }
        
        minTime = starts.min() ?? 0
        maxTime = ends.max() ?? 24
        timeRange = maxTime - minTime
        
        minTimeStr = timeStringFromDouble(minTime)
        maxTimeStr = timeStringFromDouble(maxTime)
        
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myData.count
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let c = tableView.dequeueReusableCell(withIdentifier: "sliderCell", for: indexPath) as! SliderCell
        
        let calendar = Calendar.current

        var h = calendar.component(.hour, from: myData[indexPath.row].startTime)
        var m = calendar.component(.minute, from: myData[indexPath.row].startTime)

        let s: Double = Double(h) + Double(m) / 60.0

        h = calendar.component(.hour, from: myData[indexPath.row].endTime)
        m = calendar.component(.minute, from: myData[indexPath.row].endTime)
        
        let e: Double = Double(h) + Double(m) / 60.0
        
        let sPct: Double = (s - minTime) / timeRange
        let ePct: Double = (e - minTime) / timeRange
        
        let df = DateFormatter()
        df.timeStyle = .short

        let sStr = df.string(from: myData[indexPath.row].startTime)
        let eStr = df.string(from: myData[indexPath.row].endTime)

        df.dateStyle = .short
        df.timeStyle = .none
        
        c.dateLabel.text = df.string(from: myData[indexPath.row].startTime)
        
        c.startTime = max(sPct, 0.0)
        c.endTime = min(ePct, 1.0)
        
        c.startEndLabel.text = sStr + " - " + eStr
        
        c.minLabel.text = minTimeStr
        c.maxLabel.text = maxTimeStr
        
        return c
        
    }
    func timeStringFromDouble(_ t: Double) -> String {
        
        let df = DateFormatter()
        df.timeStyle = .short
        
        var dateComponents = DateComponents()
        dateComponents.hour = Int(t)
        dateComponents.minute = Int((t - Double(Int(t))) * 60.0)
        
        var date = Calendar.current.date(from: dateComponents)!
        return df.string(from: date)

    }
}
© www.soinside.com 2019 - 2024. All rights reserved.