Swift 在 UITableViewCell 中以正确的方式使用 UISlider?
Posted
技术标签:
【中文标题】Swift 在 UITableViewCell 中以正确的方式使用 UISlider?【英文标题】:Swift Using a UISlider in a UITableViewCell the right way? 【发布时间】:2020-09-11 15:47:56 【问题描述】:我正在努力在 UITableViewCell 中正确使用 UISlider。 这是想法:
用户可以设置不同的作业,他需要完成任务。无法预先知道不同作业的数量。例如,用户 1 需要他的乐队只有吉他手和鼓手,而其他人需要吉他手、鼓手、歌手、键盘手等等。我希望他提供的是,根据他需要的工作,他可以设置特定工作所需的人数。 我在 TableViewCell 中使用 UISlider 让他这样做。
但问题来了:
(1) 用户拖动 Slider 时 UISlider 下的 Label 没有显示正确的数量
(2) prepareForReuse 函数不能正常工作。我希望用户在使用重用单元时将 Slider 返回 0 并且 Label 显示 0。
(3) 最后,jobAmountNeeded
Array 没有存储正确的值(或根本没有值),因此 Firebase 中保存的数量不正确。
在我看来,所有三个问题都连接到同一个源。
因此,如果有人可以帮助我,我会非常高兴。 :)
感谢大家的帮助!
class ProjectTeamViewController: UIViewController
// MARK: - Properties
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var headerSeperator: UIView!
@IBOutlet weak var saveButton: UIButton!
var sortedSoloJobs = [String]()
var jobIndex: Int?
var jobAmount: [String] = []
var selectedJob:String?
var sentProjectCreated:String?
var projectCreated:String?
var jobAmountNeeded = [JobNeeded]()
var slider: [SliderClass] = []
let step:Float = 1
var selectedIndexPath: IndexPath?
var tempArray = [99: "Baum"]
var tempArray2 = [99: 5]
var currentValue:Float = 0
var key = 0
override func viewDidLoad()
super.viewDidLoad()
setUpUI ()
getJobs(for: User.current) (memberJobs) in
self.tableView.reloadData()
// MARK: - Functions
func setUpUI ()
Utilities.addShadowtoView(headerSeperator)
Utilities.addShadowtoButton(saveButton)
saveButton.layer.cornerRadius = 6
saveButton.layer.borderWidth = 1
saveButton.layer.borderColor = UIColor.darkGray.cgColor
func getJobs(for user: User, completion: @escaping ([MemberJobsStruct]) -> Void)
var jobs = [String]() // All Jobs
var soloJobs = [String]() // Solo jobs from all jobs
let ref = Database.database().reference().child("team").child(user.uid)
ref.observeSingleEvent(of: .value, with: snapshot in
for case let child as DataSnapshot in snapshot.children
guard let value = child.value as? [String: Any] else
return completion ([])
let memberJob = value["memberJob"] as! String
jobs.append(memberJob)
soloJobs = Array(Set(jobs))
self.sortedSoloJobs = soloJobs.sorted()
DispatchQueue.main.async self.tableView.reloadData()
)
func json(from object:Any) -> String?
guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else
return nil
return String(data: data, encoding: String.Encoding.utf8)
@objc func sliderValueChange(sender: UISlider)
sender.isContinuous = false
currentValue = round(sender.value / step) * step
var sliderValue = Int(currentValue)
sender.value = currentValue // get slider's value
let row = sender.tag // get slider's row in table
key = row
if let row = self.jobAmountNeeded.firstIndex(where: $0.jobKey == key)
jobAmountNeeded[row].jobAmount = sliderValue
@IBAction func saveAction(_ sender: Any)
if projectCreated == nil || projectCreated == "no Value"
let alert = UIAlertController(title: "Error", message: "Internal Error send a bug report: #150420", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
self.present(alert, animated: true)
else
for value in jobAmountNeeded
let jobName = value.jobName
let jobAmount = value.jobAmount
let jobKey = value.jobKey
let currentUser = User.current
let jobreqRef = Database.database().reference().child("posts").child(currentUser.uid).child(projectCreated ?? "no Value").child("ProjectJobAmount").child(value.jobName)
let project = JobNeeded(jobName: jobName, jobAmount: jobAmount, jobKey: jobKey)
let projectDict = project.dictValue
jobreqRef.setValue(projectDict) err, jobreqRef in
// print("done")
@IBAction func cancelAction(_ sender: Any)
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
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 sortedSoloJob = sortedSoloJobs[indexPath.row]
let key = indexPath.row
let cell = tableView.dequeueReusableCell(withIdentifier: "ProjectCharacterTableViewCell", for: indexPath) as! ProjectCharacterTableViewCell
cell.jobNameLabel.text = sortedSoloJobs[indexPath.row]
jobAmountNeeded.append(JobNeeded(jobName: sortedSoloJob, jobAmount: 0, jobKey: indexPath.section))
if let row = self.jobAmountNeeded.firstIndex(where: $0.jobName == sortedSoloJob)
jobAmountNeeded[row].jobName = sortedSoloJob
tempArray.updateValue(sortedSoloJob, forKey: key)
let currentValue = cell.slider.value
cell.slider.isContinuous = false
cell.slider.tag = key
cell.slider.minimumValue = 0
cell.slider.maximumValue = Float(sortedSoloJob.count)
cell.slider.addTarget(self, action: #selector(sliderValueChange), for: .valueChanged)
cell.jobAmountLabel.text = "\(currentValue)"
return cell
// MARK: - UITableViewDelegate
extension ProjectTeamViewController: UITableViewDelegate
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
return 163
class SliderClass: NSObject
var title: String?
var subtitle: String?
var sliderMinimum: Float?
var sliderMaximum: Float?
init(title: String, subtitle: String, sliderMinimum: Float, sliderMaximum: Float)
self.title = title
self.subtitle = subtitle
self.sliderMinimum = sliderMinimum
self.sliderMaximum = sliderMaximum
class ProjectCharacterTableViewCell: UITableViewCell
@IBOutlet weak var jobAmountLabel: UILabel!
@IBOutlet weak var slider: UISlider!
@IBOutlet weak var jobNameLabel: UILabel!
override func awakeFromNib()
super.awakeFromNib()
override public func prepareForReuse()
// Ensures the reused cosmos view is as good as new
super.prepareForReuse()
self.jobAmountLabel.text = "0"
self.slider.value = 0
override func setSelected(_ selected: Bool, animated: Bool)
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
【问题讨论】:
虽然您可能是对的 - 三件事情以某种方式联系在一起 - 这是我通常会投票关闭的事情,因为看起来这些事情中的任何一件都没有联系。但我愿意帮忙。首先,我经常在表格单元格中使用滑块——它们有效。 (我想对您提出一些明确的建议,因为我不确定为什么您选择使用滑块是一个好的选择。)无论如何,把事情分解。例如,从事物中删除整个表格视图,只需将滑块和标签放在 UIView 中。 那个有用吗?如果不是,那么重复使用单元格不是问题。 感谢您的快速回复 :) 我非常感谢您没有关闭该主题并提供帮助 :) 在视图中它可以完美运行。您介意在 TableViewCell 中使用 UISlider 的地方发布您的“示例代码”吗?根据我的需要定制它,我认为我有能力......你会帮我一个大忙! 我看到你有一个你接受的答案。我仍然很乐意发布我的代码。我看到至少有两个可能使事情复杂化的差异。 (1)我不使用IB,只是直接代码。 (2) 我通过通知而不是回调传递滑块值。不管怎样,我很高兴你得到了你需要的帮助。 :-) 仍然感谢您的帮助!我是一个故事板孩子,因为我的应用程序是用于学术目的,它必须“快速简单”(至少使用直接代码更快更容易)仍然感谢您的帮助并且没有关闭主题! :) 【参考方案1】:开始更简单...
首先,让我们为您的SliderClass
添加一个“当前值”属性(我称它为SoloJob
类,因为它看起来更合乎逻辑):
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
我们将使用currentValue
属性来跟踪滑块值。
因此,创建一个带有“标题”标签、滑块和“作业数量”(或当前值)标签的单元格。我的布局是这样的:
在您的单元格类中,将滑块连接到 @IBAction
以了解它何时发生变化 - NOT 在您的控制器类中。
同样在你的单元类中,添加一个“回调”闭包变量:
// closure to tell controller the slider was changed
var callback: ((Float) -> ())?
然后,在你的 @IBAction
函数中:
@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)
回到你的控制器类,在cellForRowAt
,设置“回调”闭包:
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
当用户拖动滑块时,@IBAction func sliderValueChange()
cell 类本身将被调用,这就是我们更新单元格中标签的地方并告诉控制器值改变了。
这是一个完整的实现:
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
和情节提要源(带有所有@IBOutlet
和@IBAction
连接):
<?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" />
<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" />
<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" />
<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" />
<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" />
<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" />
<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" />
<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" />
<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" />
<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>
结果:
而且因为每当滑块改变时我们都会更新我们的数据数组,所以我们可以滚动表格并正确配置重复使用的单元格。
当所有这些都有意义时,将这些方法带到您的项目中以匹配您的布局和数据结构。
【讨论】:
嘿@DonMag 我真是无语了!谢谢你这个令人难以置信的答案!它功能完美,如果没有您的回答,我会很头疼!希望它也可以帮助其他人! :)以上是关于Swift 在 UITableViewCell 中以正确的方式使用 UISlider?的主要内容,如果未能解决你的问题,请参考以下文章
在嵌入在 UITableviewCell 中的 UICollectionView 中进行 Segue,Swift
如何在 Swift 中检测对 UITableViewCell 的 UIImageView 的触摸?
UITableViewCell 中的 UICollectionView 在 swift 3 中滚动不流畅