Ios 在UITableView swift中限制每个节的单元格选择
我想限制我的tableview的某些部分只允许选择一个单元格,从现在起,我的所有单元格都可以被选择,而不管它位于哪个部分 然而,有一点扭曲:我的部分是一个[array],根据不同的变量动态变化 我的每个部分本身都是一个变量,因此我可以通过编程方式对它们进行精确定位,如下所示:Ios 在UITableView swift中限制每个节的单元格选择,ios,swift,uitableview,swift3,Ios,Swift,Uitableview,Swift3,我想限制我的tableview的某些部分只允许选择一个单元格,从现在起,我的所有单元格都可以被选择,而不管它位于哪个部分 然而,有一点扭曲:我的部分是一个[array],根据不同的变量动态变化 我的每个部分本身都是一个变量,因此我可以通过编程方式对它们进行精确定位,如下所示: var section1 = [NSDictionary(objects: [NSLocalizedString("Alcohol use less than 24 hours", comment:""), 2], ED
var section1 = [NSDictionary(objects: [NSLocalizedString("Alcohol use less than 24 hours", comment:""), 2],
EDIT2:有人指出我可以创建一个包含限制的var
var restrictedSections: [[NSDictionary]: Bool] {return [section1: true,section2: true,section3: true, section4: true, section4COPI: true, section5: true, section5COPI: true, section6: false, section7: false, section8: false] }
这不能作为IndexPath引用,因此这里没有运气。。。但也许是在正确的道路上
tableView中的一些代码(为StackOverflow阅读器简化):
正如你所看到的,当我点击一个单元格时,它会添加一个值,单元格会改变背景颜色并添加一个复选标记
我需要做的是,如果有一个单元格被选中,在某些部分中,只有1个单元格可以被选中,它需要检查该部分中是否有任何单元格被选中,并取消选中,以支持用户点击的新单元格。现在我根本不知道该怎么做
感谢您对第2项的帮助,您可以这样做。取消选择后,您将更改该单元格的
accessoryType
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
let cell = tableView.cellForRow(at: indexPath)
for selectedIndexPath in tableview.indexPathForSelectedRow {
if selectedIndexPath.section == indexPath.section {
tableview.deselectRow(at: indexPath, animated: true)
cell?.accessoryType = .none
}
}
return indexPath
}
在selectedIndexPaths数组中添加新选定行的索引路径之前,可以添加一行
self.selectedIndexPaths = self.selectedIndexPaths.filter({$0.section != indexPath.section})
self.selectedIndexPaths.append(indexPath)
重新加载tableView时,由于cellForRow中的if-else块,同一节中先前选定的单元格的外观将发生更改。基本上,tableView的最佳解决方案(IMHO)是为表创建视图模型,根据需要操作数据,然后在表中反映该数据。然后,尽一切可能让表对数据更改做出反应,而不是试图使用表视图本身来反映数据或状态 编辑:代码不再使用
reloadData
,而是使用performBatchUpdates
进行更优雅的演示
我创建了一个项目,你可以找到它
视图数据包含在此处:
let pilots = "Pilots"
let crew = "Crew"
let passengers = "Passengers"
var sections: [String] = []
var multipleSelectionsAllowed: Set<String> = []
var members: [String: [String]] = [:]
var selectedMembers: Set<String> = []
数据是以编程方式创建的,请参阅下面附加的项目或完整代码
您说过部分可能会更改,因此部分
是一个变量,我们稍后将对其进行更改
selectedMembers
包含类型的散列(即飞行员、机组人员或乘客及其姓名),因此它应该是唯一的。此数组将当前选择作为数据而不是索引来反映
但是,我们需要IndExpath来反映isSelected
UI更改:好的,我们将为此使用两个函数:
typealias KeyToValues = (section: String, name: String)
func sectionNameToHash(section: String, name: String) -> String {
let hash = section + "|" + name
return hash
}
func hashToSectionName(hash: String) -> KeyToValues {
let array = hash.components(separatedBy: "|")
assert(array.count == 2)
return (array[0], array[1])
}
另外,我发现在过去非常有用的一点是,将更改单元格外观的代码放在一个地方,并在创建或更改单元格时调用它。随着UI的更改,您不会失去同步
func updateCell(atIndexPath indexPath: IndexPath) {
let cells = tableView.visibleCells
for cell in cells {
guard let path = tableView.indexPath(for: cell) else { continue }
if path == indexPath {
updateCell(cell, atIndexPath: indexPath)
}
}
}
func updateCell(_ cell: UITableViewCell, atIndexPath indexPath: IndexPath) {
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
let hash = sectionNameToHash(section: section, name: name)
let shouldBeSelected = selectedMembers.contains(hash)
if shouldBeSelected {
cell.accessoryType = .checkmark
print("SELECTED", hash)
} else {
cell.accessoryType = .none
print("DESELECTED", hash)
}
}
两者都需要,因为在某些情况下,您只有一个indexPath,而没有单元格
请注意,在创建单元时使用上述方法:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
cell.textLabel?.text = name
updateCell(cell, atIndexPath: indexPath)
return cell
}
当tableView检测到选择时,您将首先查看现有的选定数据,然后从数据中删除该选择,然后更新任何删除的单元格的UI:
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let canMultipleSelect = multipleSelectionsAllowed.contains(section)
if !canMultipleSelect, let paths = tableView.indexPathsForSelectedRows {
for path in paths {
if path.section == indexPath.section {
let name = names[path.row]
let hash = sectionNameToHash(section: section, name: name)
selectedMembers.remove(hash)
updateCell(atIndexPath: path)
tableView.deselectRow(at: path, animated: true)
}
}
}
return indexPath
}
然后,处理选择方法:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
let hash = sectionNameToHash(section: section, name: name)
selectedMembers.insert(hash)
print("SELECTED THE CELL AT", hash)
updateCell(atIndexPath: indexPath)
}
瞧,一切都按照你的意愿进行。但是,更好的是,你可以按照你说的那样重新安排部分,并让所有内容都被正确地选择。示例代码在你选择第一行/列5秒钟后重新安排部分
if indexPath.section == 0 && indexPath.row == 0 {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.sections = [self.crew, self.pilots, self.passengers] // changed!
tableView.reloadData()
// all selections from the tableView are now gone
// NOTE: none of the other data changes!
for hash in self.selectedMembers {
let value = self.hashToSectionName(hash: hash)
guard
let sectionNumber = self.sections.firstIndex(of: value.section),
let names = self.members[value.section],
let row = names.firstIndex(of: value.name)
else { fatalError() }
let indexPath = IndexPath(row: row, section: sectionNumber)
self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
}
}
}
重载()删除所有选择,因此上述代码使用已知的选定成员通知tableView选择列表,即使每个选择的单元格不可见
全班
import UIKit
private final class MyCell: UITableViewCell {
override var reuseIdentifier: String? { "cell" }
}
final class ViewController: UITableViewController {
let pilots = "Pilots"
let crew = "Crew"
let passengers = "Passengers"
var sections: [String] = []
var multipleSelectionsAllowed: Set<String> = []
var members: [String: [String]] = [:]
var selectedMembers: Set<String> = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MyCell.self, forCellReuseIdentifier: "cell")
tableView.allowsMultipleSelection = true
sections = [pilots, crew, passengers] // initial ordering of sections
multipleSelectionsAllowed = [passengers]
constructData()
}
private func constructData() {
var array: [String] = []
(1..<6).forEach { array.append("Pilot \($0)")}
members[pilots] = array
array.removeAll()
(1..<20).forEach { array.append("Crew \($0)")}
members[crew] = array
array.removeAll()
(1..<250).forEach { array.append("Passenger \($0)")}
members[passengers] = array
}
// MARK: - Helpers -
typealias KeyToValues = (section: String, name: String)
func sectionNameToHash(section: String, name: String) -> String {
let hash = section + "|" + name
return hash
}
func hashToSectionName(hash: String) -> KeyToValues {
let array = hash.components(separatedBy: "|")
assert(array.count == 2)
return (array[0], array[1])
}
}
extension ViewController /*: UITableViewDataSource */ {
override func numberOfSections(in: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let type = sections[section]
let count = members[type]?.count ?? 0 // could use guard here too and crash if nil
return count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
cell.textLabel?.text = name
updateCell(cell, atIndexPath: indexPath)
return cell
}
func updateCell(atIndexPath indexPath: IndexPath) {
let cells = tableView.visibleCells
for cell in cells {
guard let path = tableView.indexPath(for: cell) else { continue }
if path == indexPath {
updateCell(cell, atIndexPath: indexPath)
}
}
}
func updateCell(_ cell: UITableViewCell, atIndexPath indexPath: IndexPath) {
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
let hash = sectionNameToHash(section: section, name: name)
let shouldBeSelected = selectedMembers.contains(hash)
if shouldBeSelected {
cell.accessoryType = .checkmark
print("SELECTED", hash)
} else {
cell.accessoryType = .none
print("DESELECTED", hash)
}
}
}
extension ViewController /* : UITableViewDelegate */ {
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let canMultipleSelect = multipleSelectionsAllowed.contains(section)
if !canMultipleSelect, let paths = tableView.indexPathsForSelectedRows {
for path in paths {
if path.section == indexPath.section {
let name = names[path.row]
let hash = sectionNameToHash(section: section, name: name)
selectedMembers.remove(hash)
updateCell(atIndexPath: path)
tableView.deselectRow(at: path, animated: true)
}
}
}
return indexPath
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
let hash = sectionNameToHash(section: section, name: name)
selectedMembers.insert(hash)
print("SELECTED THE CELL AT", hash)
updateCell(atIndexPath: indexPath)
if indexPath.section == 0 && indexPath.row == 0 {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.sections = [self.crew, self.pilots, self.passengers]
tableView.reloadData()
// all selections from the tableView are gone
for hash in self.selectedMembers {
let value = self.hashToSectionName(hash: hash)
guard
let sectionNumber = self.sections.firstIndex(of: value.section),
let names = self.members[value.section],
let row = names.firstIndex(of: value.name)
else { fatalError() }
let indexPath = IndexPath(row: row, section: sectionNumber)
self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
}
}
}
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
print("DESELECTED THE CELL AT", hash)
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
let hash = sectionNameToHash(section: section, name: name)
selectedMembers.remove(hash)
updateCell(atIndexPath: indexPath)
}
}
导入UIKit
私有最终类MyCell:UITableViewCell{
重写变量reuseIdentifier:字符串?{“单元格”}
}
最终类ViewController:UITableViewController{
让飞行员=“飞行员”
let crew=“crew”
让乘客=“乘客”
变量节:[字符串]=[]
var multipleSelectionsAllowed:Set=[]
变量成员:[String:[String]]=[:]
var selectedMembers:Set=[]
重写func viewDidLoad(){
super.viewDidLoad()
tableView.register(MyCell.self,forCellReuseIdentifier:“cell”)
tableView.allowsMultipleSelection=true
区段=[飞行员、机组人员、乘客]//区段的初始顺序
multipleSelectionsAllowed=[乘客]
constructData()
}
私有函数构造函数数据(){
变量数组:[String]=[]
(1..Int{
返回节数
}
重写func tableView(tableView:UITableView,numberofrowsinssection:Int)->Int{
let type=截面[截面]
let count=members[type]?.count±0//也可以在此处使用保护,如果为nil,则崩溃
返回计数
}
重写func tableView(tableView:UITableView,cellForRowAt indexath:indexPath)->UITableViewCell{
let cell=tableView.dequeueReusableCell(带有标识符:“cell”,用于:indexath)
let section=sections[indexPath.section]
guard let name=members[section]else{fatalError()}
让name=names[indexPath.row]
cell.textlab?.text=名称
updateCell(单元格,atIndexPath:indexPath)
返回单元
}
func updateCell(atIndexPath indexath:indexath){
let cells=tableView.visibleCells
细胞中的细胞{
guard let path=tableView.indexPath(for:cell)else{continue}
如果路径==indexPath{
updateCell(单元格,atIndexPath:indexPath)
}
}
}
func updateCell(cell:UITableViewCell,atIndexPath indexPath:indexPath){
let section=sections[indexPath.section]
guard let name=members[section]else{fatalError()}
让name=names[indexPath.row]
让hash=sectionnametohsh(section:section,name:name)
让我们选吧
if indexPath.section == 0 && indexPath.row == 0 {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.sections = [self.crew, self.pilots, self.passengers] // changed!
tableView.reloadData()
// all selections from the tableView are now gone
// NOTE: none of the other data changes!
for hash in self.selectedMembers {
let value = self.hashToSectionName(hash: hash)
guard
let sectionNumber = self.sections.firstIndex(of: value.section),
let names = self.members[value.section],
let row = names.firstIndex(of: value.name)
else { fatalError() }
let indexPath = IndexPath(row: row, section: sectionNumber)
self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
}
}
}
import UIKit
private final class MyCell: UITableViewCell {
override var reuseIdentifier: String? { "cell" }
}
final class ViewController: UITableViewController {
let pilots = "Pilots"
let crew = "Crew"
let passengers = "Passengers"
var sections: [String] = []
var multipleSelectionsAllowed: Set<String> = []
var members: [String: [String]] = [:]
var selectedMembers: Set<String> = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MyCell.self, forCellReuseIdentifier: "cell")
tableView.allowsMultipleSelection = true
sections = [pilots, crew, passengers] // initial ordering of sections
multipleSelectionsAllowed = [passengers]
constructData()
}
private func constructData() {
var array: [String] = []
(1..<6).forEach { array.append("Pilot \($0)")}
members[pilots] = array
array.removeAll()
(1..<20).forEach { array.append("Crew \($0)")}
members[crew] = array
array.removeAll()
(1..<250).forEach { array.append("Passenger \($0)")}
members[passengers] = array
}
// MARK: - Helpers -
typealias KeyToValues = (section: String, name: String)
func sectionNameToHash(section: String, name: String) -> String {
let hash = section + "|" + name
return hash
}
func hashToSectionName(hash: String) -> KeyToValues {
let array = hash.components(separatedBy: "|")
assert(array.count == 2)
return (array[0], array[1])
}
}
extension ViewController /*: UITableViewDataSource */ {
override func numberOfSections(in: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let type = sections[section]
let count = members[type]?.count ?? 0 // could use guard here too and crash if nil
return count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
cell.textLabel?.text = name
updateCell(cell, atIndexPath: indexPath)
return cell
}
func updateCell(atIndexPath indexPath: IndexPath) {
let cells = tableView.visibleCells
for cell in cells {
guard let path = tableView.indexPath(for: cell) else { continue }
if path == indexPath {
updateCell(cell, atIndexPath: indexPath)
}
}
}
func updateCell(_ cell: UITableViewCell, atIndexPath indexPath: IndexPath) {
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
let hash = sectionNameToHash(section: section, name: name)
let shouldBeSelected = selectedMembers.contains(hash)
if shouldBeSelected {
cell.accessoryType = .checkmark
print("SELECTED", hash)
} else {
cell.accessoryType = .none
print("DESELECTED", hash)
}
}
}
extension ViewController /* : UITableViewDelegate */ {
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let canMultipleSelect = multipleSelectionsAllowed.contains(section)
if !canMultipleSelect, let paths = tableView.indexPathsForSelectedRows {
for path in paths {
if path.section == indexPath.section {
let name = names[path.row]
let hash = sectionNameToHash(section: section, name: name)
selectedMembers.remove(hash)
updateCell(atIndexPath: path)
tableView.deselectRow(at: path, animated: true)
}
}
}
return indexPath
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
let hash = sectionNameToHash(section: section, name: name)
selectedMembers.insert(hash)
print("SELECTED THE CELL AT", hash)
updateCell(atIndexPath: indexPath)
if indexPath.section == 0 && indexPath.row == 0 {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.sections = [self.crew, self.pilots, self.passengers]
tableView.reloadData()
// all selections from the tableView are gone
for hash in self.selectedMembers {
let value = self.hashToSectionName(hash: hash)
guard
let sectionNumber = self.sections.firstIndex(of: value.section),
let names = self.members[value.section],
let row = names.firstIndex(of: value.name)
else { fatalError() }
let indexPath = IndexPath(row: row, section: sectionNumber)
self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
}
}
}
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
print("DESELECTED THE CELL AT", hash)
let section = sections[indexPath.section]
guard let names = members[section] else { fatalError() }
let name = names[indexPath.row]
let hash = sectionNameToHash(section: section, name: name)
selectedMembers.remove(hash)
updateCell(atIndexPath: indexPath)
}
}