Swift Using a UISlider in a UITableViewCell the right way?

前端 未结 1 1903
南旧
南旧 2021-01-17 07:24

I am struggling to properly use UISliders in a UITableViewCell. Here is the Idea:

  • The User can set different Jobs which he needs to fulfil tasks. The amount of
相关标签:
1条回答
  • 2021-01-17 07:43

    Start simpler...

    First, let's add a "current value" property to your SliderClass (I'm calling it a SoloJob class, as it seems more logical):

    class SoloJob: NSObject {
        var title: String = ""
        var subtitle: String = ""
        var sliderMinimum: Float = 0
        var sliderMaximum: Float = 100
        var currentValue: Float = 0
        
        init(title: String, subtitle: String, sliderMinimum: Float, sliderMaximum: Float, currentValue: Float) {
            self.title = title
            self.subtitle = subtitle
            self.sliderMinimum = sliderMinimum
            self.sliderMaximum = sliderMaximum
            self.currentValue = currentValue
        }
    }
    

    We'll use the currentValue property to keep track of the slider value.

    So, create a cell with a "title" label, a slider, and a "job amount" (or current value) label. I have it laid out like this:

    In your cell class, connect the slider to an @IBAction for when it changes - NOT in your controller class.

    Also in your cell class, add a "callback" closure var:

    // closure to tell controller the slider was changed
    var callback: ((Float) -> ())?
    

    then, in your @IBAction func:

    @IBAction func sliderValueChange(_ sender: UISlider) -> Void {
        let v = sender.value
        // update the label
        jobAmountLabel.text = "Current Amount: \(Int(v))"
        // tell the controller the slider changed
        callback?(v)
    }
    

    Back in your controller class, in cellForRowAt, setup the "callback" closure:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "ProjectCharacterTableViewCell", for: indexPath) as! ProjectCharacterTableViewCell
        
        let thisJob: SoloJob = sortedSoloJobs[indexPath.row]
        
        // set values / labels in the cell
        
        // closure to get notified when the slider is changed
        cell.callback = { val in
            // update our data
            self.sortedSoloJobs[indexPath.row].currentValue = val
        }
    
        return cell
    
    }
    

    When the user drags the slider, @IBAction func sliderValueChange() in the cell class itself will be called, and that's where we update the label in the cell and tell the controller the value changed.

    Here is a complete implementation:

    import UIKit
    
    class SoloJob: NSObject {
        var title: String = ""
        var subtitle: String = ""
        var sliderMinimum: Float = 0
        var sliderMaximum: Float = 100
        var currentValue: Float = 0
        
        init(title: String, subtitle: String, sliderMinimum: Float, sliderMaximum: Float, currentValue: Float) {
            self.title = title
            self.subtitle = subtitle
            self.sliderMinimum = sliderMinimum
            self.sliderMaximum = sliderMaximum
            self.currentValue = currentValue
        }
    }
    
    class ProjectCharacterTableViewCell: UITableViewCell {
        
        @IBOutlet weak var jobAmountLabel: UILabel!
        @IBOutlet weak var slider: UISlider!
        @IBOutlet weak var jobNameLabel: UILabel!
        
        // closure to tell controller the slider was changed
        var callback: ((Float) -> ())?
        
        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 {
            contentView.layer.cornerRadius = 12
            contentView.layer.borderColor = UIColor.blue.cgColor
            contentView.layer.borderWidth = 1
            contentView.layer.masksToBounds = true
            contentView.backgroundColor = .white
            backgroundColor = .clear
        }
        func configureCell(_ theJob: SoloJob) -> Void {
            jobNameLabel.text = theJob.title + " - min: \(Int(theJob.sliderMinimum)) / max: \(Int(theJob.sliderMaximum))"
            slider.minimumValue = theJob.sliderMinimum
            slider.maximumValue = theJob.sliderMaximum
            slider.value = theJob.currentValue
            jobAmountLabel.text = "Current Amount: \(Int(theJob.currentValue))"
        }
        
        // connect valueChanged action in Storyboard
        @IBAction func sliderValueChange(_ sender: UISlider) -> Void {
            let v = sender.value
            // update the label
            jobAmountLabel.text = "Current Amount: \(Int(v))"
            // tell the controller the slider changed
            callback?(v)
        }
    }
    
    class ProjectTeamViewController: UIViewController {
    
        @IBOutlet weak var tableView: UITableView!
    
        var sortedSoloJobs: [SoloJob] = []
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // create some example data
            for i in 1...20 {
                // random slider minimum between 0 and 2
                let minVal = Int.random(in: 0...2)
                // random slider maximum between 5 and 10
                let maxVal = Int.random(in: 5...10)
                // start with current value at minimum
                let curVal = minVal
                let job = SoloJob(title: "Job Name \(i)", subtitle: "", sliderMinimum: Float(minVal), sliderMaximum: Float(maxVal), currentValue: Float(curVal))
                sortedSoloJobs.append(job)
            }
            
            tableView.dataSource = self
            tableView.delegate = self
            
        }
     
    }
    
    extension ProjectTeamViewController: UITableViewDataSource {
        
        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return sortedSoloJobs.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            
            let cell = tableView.dequeueReusableCell(withIdentifier: "ProjectCharacterTableViewCell", for: indexPath) as! ProjectCharacterTableViewCell
            
            let thisJob: SoloJob = sortedSoloJobs[indexPath.row]
            
            cell.configureCell(thisJob)
            
            // closure to get notified when the slider is changed
            cell.callback = { val in
                // update our data
                self.sortedSoloJobs[indexPath.row].currentValue = val
            }
    
            return cell
    
        }
        
    }
    
    extension ProjectTeamViewController: UITableViewDelegate {
    }
    

    and the Storyboard source (with all the @IBOutlet and @IBAction connections):

    <?xml version="1.0" encoding="UTF-8"?>
    <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="OoM-UM-qa5">
        <device id="retina4_0" orientation="portrait" appearance="light"/>
        <dependencies>
            <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
            <capability name="Safe area layout guides" minToolsVersion="9.0"/>
            <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
        </dependencies>
        <scenes>
            <!--Project Team View Controller-->
            <scene sceneID="LA9-sV-8lR">
                <objects>
                    <viewController id="OoM-UM-qa5" customClass="ProjectTeamViewController" customModule="TableAdd" customModuleProvider="target" sceneMemberID="viewController">
                        <view key="view" contentMode="scaleToFill" id="GWK-to-6GG">
                            <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                            <subviews>
                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="I5Z-lW-4b3">
                                    <rect key="frame" x="20" y="20" width="280" height="30"/>
                                    <color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                    <state key="normal" title="SAVE BUTTON"/>
                                </button>
                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Rkw-MO-6Op" userLabel="Horizontal Line View">
                                    <rect key="frame" x="20" y="58" width="280" height="1"/>
                                    <color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                    <constraints>
                                        <constraint firstAttribute="height" constant="1" id="BkU-lx-Zp8"/>
                                    </constraints>
                                </view>
                                <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="pgu-lS-tk6">
                                    <rect key="frame" x="20" y="67" width="280" height="481"/>
                                    <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
                                    <prototypes>
                                        <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="ProjectCharacterTableViewCell" rowHeight="109" id="tnK-1p-f4N" customClass="ProjectCharacterTableViewCell" customModule="TableAdd" customModuleProvider="target">
                                            <rect key="frame" x="0.0" y="28" width="280" height="109"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="tnK-1p-f4N" id="gcG-sV-dlw">
                                                <rect key="frame" x="0.0" y="0.0" width="280" height="109"/>
                                                <autoresizingMask key="autoresizingMask"/>
                                                <subviews>
                                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Job Name Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="l3R-9V-mjm">
                                                        <rect key="frame" x="78" y="11" width="124" height="21"/>
                                                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                        <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="mj1-CV-iWZ">
                                                        <rect key="frame" x="13" y="36" width="254" height="31"/>
                                                        <connections>
                                                            <action selector="sliderValueChange:" destination="tnK-1p-f4N" eventType="valueChanged" id="RkI-oL-0eQ"/>
                                                        </connections>
                                                    </slider>
                                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Job Amount Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xMH-9r-GO9">
                                                        <rect key="frame" x="70.5" y="70" width="139" height="21"/>
                                                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                        <nil key="textColor"/>
                                                        <nil key="highlightedColor"/>
                                                    </label>
                                                </subviews>
                                                <constraints>
                                                    <constraint firstItem="l3R-9V-mjm" firstAttribute="top" secondItem="gcG-sV-dlw" secondAttribute="topMargin" id="DPl-Kl-d1J"/>
                                                    <constraint firstItem="mj1-CV-iWZ" firstAttribute="leading" secondItem="gcG-sV-dlw" secondAttribute="leadingMargin" id="Sx7-a7-Yxy"/>
                                                    <constraint firstItem="mj1-CV-iWZ" firstAttribute="top" secondItem="l3R-9V-mjm" secondAttribute="bottom" constant="4" id="Z05-fI-eal"/>
                                                    <constraint firstItem="xMH-9r-GO9" firstAttribute="top" secondItem="mj1-CV-iWZ" secondAttribute="bottom" constant="4" id="a8n-XL-xxa"/>
                                                    <constraint firstAttribute="bottomMargin" relation="greaterThanOrEqual" secondItem="xMH-9r-GO9" secondAttribute="bottom" id="cg5-O7-mnS"/>
                                                    <constraint firstItem="l3R-9V-mjm" firstAttribute="centerX" secondItem="gcG-sV-dlw" secondAttribute="centerX" id="hGU-ad-se2"/>
                                                    <constraint firstItem="xMH-9r-GO9" firstAttribute="centerX" secondItem="gcG-sV-dlw" secondAttribute="centerX" id="p4W-nU-hxy"/>
                                                    <constraint firstAttribute="trailingMargin" secondItem="mj1-CV-iWZ" secondAttribute="trailing" id="umL-5D-BUa"/>
                                                </constraints>
                                            </tableViewCellContentView>
                                            <connections>
                                                <outlet property="jobAmountLabel" destination="xMH-9r-GO9" id="AIQ-ro-Q2C"/>
                                                <outlet property="jobNameLabel" destination="l3R-9V-mjm" id="cA7-Kq-aRd"/>
                                                <outlet property="slider" destination="mj1-CV-iWZ" id="YDo-wV-0rA"/>
                                            </connections>
                                        </tableViewCell>
                                    </prototypes>
                                </tableView>
                            </subviews>
                            <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
                            <constraints>
                                <constraint firstItem="I5Z-lW-4b3" firstAttribute="leading" secondItem="iax-Rw-gHC" secondAttribute="leading" constant="20" id="8bZ-vh-e2L"/>
                                <constraint firstItem="pgu-lS-tk6" firstAttribute="leading" secondItem="iax-Rw-gHC" secondAttribute="leading" constant="20" id="KK9-MN-7TR"/>
                                <constraint firstItem="I5Z-lW-4b3" firstAttribute="top" secondItem="iax-Rw-gHC" secondAttribute="top" constant="20" id="MBM-in-OG7"/>
                                <constraint firstItem="Rkw-MO-6Op" firstAttribute="leading" secondItem="iax-Rw-gHC" secondAttribute="leading" constant="20" id="NM3-Ah-IIR"/>
                                <constraint firstItem="pgu-lS-tk6" firstAttribute="top" secondItem="Rkw-MO-6Op" secondAttribute="bottom" constant="8" id="abw-Kr-4qh"/>
                                <constraint firstItem="iax-Rw-gHC" firstAttribute="bottom" secondItem="pgu-lS-tk6" secondAttribute="bottom" constant="20" id="dBK-83-lBg"/>
                                <constraint firstItem="iax-Rw-gHC" firstAttribute="trailing" secondItem="I5Z-lW-4b3" secondAttribute="trailing" constant="20" id="erM-u3-zLO"/>
                                <constraint firstItem="Rkw-MO-6Op" firstAttribute="top" secondItem="I5Z-lW-4b3" secondAttribute="bottom" constant="8" id="ry1-D5-U89"/>
                                <constraint firstItem="iax-Rw-gHC" firstAttribute="trailing" secondItem="Rkw-MO-6Op" secondAttribute="trailing" constant="20" id="vhe-jw-Dnb"/>
                                <constraint firstItem="iax-Rw-gHC" firstAttribute="trailing" secondItem="pgu-lS-tk6" secondAttribute="trailing" constant="20" id="zdu-4p-FAt"/>
                            </constraints>
                            <viewLayoutGuide key="safeArea" id="iax-Rw-gHC"/>
                        </view>
                        <connections>
                            <outlet property="tableView" destination="pgu-lS-tk6" id="08E-xc-PqA"/>
                        </connections>
                    </viewController>
                    <placeholder placeholderIdentifier="IBFirstResponder" id="Ns0-iW-ioz" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
                </objects>
                <point key="canvasLocation" x="-534.375" y="839.78873239436621"/>
            </scene>
        </scenes>
    </document>
    

    Result:

    And because we're updating our data array whenever a slider is changed, we can scroll through the table and reused cells will be configured properly.

    When all of that makes sense, carry the methods over to your project to match your layout and data structuring.

    0 讨论(0)
提交回复
热议问题