我正在尝试使用UITableView。基本上,单元格在选中时会扩展,所有其他单元格应该处于原始高度。但不知怎的,他们没有。
代码:
@IBOutlet weak var searchBar: UISearchBar!
// MARK: Instance Variables
let database = DatabaseHandler.instance
var allLoops = [SongLoop]()
var sliderTimer: Timer!
var loopPlayer: LoopPlayer!
var overlayView: Overlay!
var overlayViewLayer: CAShapeLayer!
var openedIndexPath: IndexPath!
// MARK: IBOutlet properties
@IBOutlet weak var emptyView: UIView!
@IBOutlet weak var tblExpandable: UITableView!
@IBOutlet weak var addNewLoop: UIButton!
// MARK: ViewController lifecycles
override func viewDidLoad() {
super.viewDidLoad()
emptyView.isHidden = true
addNewLoop.isHidden = true
setUpOverlayView()
let height = UIApplication.shared.statusBarFrame.size.height
tblExpandable.contentInset = UIEdgeInsets(top: height, left: 0, bottom: 0, right: 0)
// Change color status bar
self.setNeedsStatusBarAppearanceUpdate()
self.tblExpandable.alwaysBounceVertical = false
self.searchBar.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let loadDatabaseThread = DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated)
loadDatabaseThread.async {
self.allLoops = self.database.getLoops()
DispatchQueue.main.async {
self.tblExpandable.reloadData()
self.updateLoopTimeOnSlider()
if self.openedIndexPath != nil {
self.tblExpandable.reloadRows(at: [self.openedIndexPath!], with: .fade)
self.loopPlayer = LoopPlayer(loop: self.allLoops[self.openedIndexPath.row])
}
}
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
allLoops.removeAll()
stopUpdatingThumbPositionOnLoopSlider()
stopPlayingLoop()
}
// MARK: UITableView Delegate and Datasource Functions
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if openedIndexPath != nil {
var rect = overlayView.bounds
rect.origin.y = scrollView.contentOffset.y;
overlayView.frame = rect
let cell = self.tblExpandable.cellForRow(at: openedIndexPath)
if (cell != nil) {
let cellRect = overlayView.convert(cell!.frame, from: self.tblExpandable)
overlayView.selectedCellRect = cellRect
let topRect = CGRect(x: 0, y: 0, width: cellRect.size.width, height: cellRect.origin.y)
let bottomRect = CGRect(x: 0, y: cellRect.maxY - 1, width: cellRect.size.width, height: overlayView.frame.size.height - cellRect.maxY)
let path = UIBezierPath(rect: topRect)
let bottomPath = UIBezierPath(rect: bottomRect)
path.append(bottomPath)
overlayViewLayer.path = path.cgPath
overlayView.layer.mask = overlayViewLayer
}
}
}
func tableView(
_ tableView: UITableView,
numberOfRowsInSection section: Int
) -> Int {
let count = allLoops.count
if count == 0 {
emptyView.isHidden = false
addNewLoop.isHidden = false
} else {
addNewLoop.isHidden = true
emptyView.isHidden = true
}
return count
}
func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: "cellSong",
for: indexPath) as! LoopMainViewCell
let bgColorView = UIView()
bgColorView.backgroundColor = Colors.selectedCellBackground
cell.selectedBackgroundView = bgColorView
let currentLoop = self.allLoops[indexPath.row]
cell.songLabel.text = currentLoop.name
cell.artistLabel.text = currentLoop.song.artist
cell.durationLabel.text = formatDuration(Int(currentLoop.duration))
cell.dateLabel.text = dateFormatter.string(from: currentLoop.updatedDate as Date)
cell.slider.value = 0.0
cell.slider.minimumValue = 0.0
cell.slider.maximumValue = Float(currentLoop.end - currentLoop.start)
cell.selectionStyle = .none
cell.playButton.setImage(Images.playButton, for: UIControlState())
return cell
}
func tableView(
_ tableView: UITableView,
heightForRowAt indexPath: IndexPath
) -> CGFloat {
return openedIndexPath != nil
&& openedIndexPath == indexPath
&& openedIndexPath.row < allLoops.count ? 208 : 117
}
func tableView(
_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath
) {
if searchBar.isFirstResponder {
searchBar.resignFirstResponder()
searchBar.endEditing(true)
searchBar.setShowsCancelButton(false, animated: true)
}
// Prevent reloading cell when it is expanded
if tableView.rectForRow(at: indexPath).size.height == 117 {
openedIndexPath = indexPath
self.loopPlayer = LoopPlayer(loop: allLoops[indexPath.row])
tableView.reloadRows(at: [openedIndexPath], with: .fade)
overlayView.alpha = 0
tableView.addSubview(overlayView)
self.scrollViewDidScroll(tableView)
UIView.animate(withDuration: 0.3, animations: {
self.overlayView.alpha = 0.5
})
}
}
// MARK: Event Handler Delegates
override func remoteControlReceived(with event: UIEvent?) {
guard let event = event else {
print("No event received.")
return
}
guard event.type == UIEventType.remoteControl else {
print("Received non Remote Control event.")
return
}
switch event.subtype {
case UIEventSubtype.remoteControlPlay:
playLoop(playButton)
case UIEventSubtype.remoteControlPause:
pauseLoop(playButton)
case UIEventSubtype.remoteControlTogglePlayPause:
playLoop(playButton)
default: break
}
}
// MARK Delete action
@IBAction func deleteLoop(_ sender: AnyObject) {
let item = loopPlayer.currentLoop
let selectedIndexPath = openedIndexPath
let title = "Delete \(item.name)?"
let ac = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
// Delete action
let deleteAction = UIAlertAction(title: title, style: .destructive) {
(action) -> Void in
// Need method to delete loop
self.stopPlayingLoop()
self.database.deleteLoops([self.loopPlayer.currentLoop])
self.allLoops.remove(at: self.openedIndexPath.row)
self.tblExpandable.deleteRows(at: [self.openedIndexPath], with: .automatic)
self.openedIndexPath = nil
if (selectedIndexPath?.row)! < self.allLoops.count {
self.tblExpandable.reloadRows(at: [selectedIndexPath!], with: .fade)
self.tblExpandable.reloadData()
}
self.overlayView.removeFromSuperview()
}
// Cancel action
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
// Add action
ac.addAction(deleteAction)
ac.addAction(cancelAction)
present(ac, animated: true, completion: nil)
}
// MARK: Custom function
func setUpOverlayView() {
overlayView = Overlay(frame: self.view.bounds)
overlayView.backgroundColor = UIColor.black
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action:#selector(LoopTableViewController.tapGestureRecognized(_:)))
tapGestureRecognizer.cancelsTouchesInView = false
self.overlayView.addGestureRecognizer(tapGestureRecognizer)
overlayViewLayer = CAShapeLayer()
overlayViewLayer.frame = overlayView.bounds
overlayViewLayer.masksToBounds = true
}
func tapGestureRecognized(_ tapGesture: UITapGestureRecognizer) {
let selectedIndexPath = openedIndexPath
openedIndexPath = nil
if selectedIndexPath != nil {
self.tblExpandable.reloadRows(at: [selectedIndexPath!], with: .fade)
}
stopPlayingLoop()
UIView.animate(
withDuration: 0.3,
animations: { self.overlayView.alpha = 0.0 },
completion: { finished in self.overlayView.removeFromSuperview()})
}
// MARK: Formatter
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}()
func formatDuration(_ duration: Int) -> String {
let hour = duration / 3600
let minutes = duration / 60
let seconds = duration % 60
return NSString.localizedStringWithFormat("%02d:%02d:%02d", hour, minutes, seconds) as String
}
@IBAction func cancelToMainController(_ segue: UIStoryboardSegue){
}
// MARK: Music Player Actions
@IBAction func playLoopAtTime(_ sender: UISlider) {
loopPlayer.pause()
loopPlayer.playAt(Double(sender.value))
}
@IBAction func playMusic(_ sender: UIButton) {
if sender.currentImage == Images.playButton {
playLoop(sender)
} else {
pauseLoop(sender)
}
}
// MARK: Segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Segue.editExistingLoopIdentifier {
if let destination = segue.destination as? EditLoopViewController {
destination.loop = loopPlayer.currentLoop
}
}
}
答案 0 :(得分:0)
只需使用以下方法就可以做到这一点
-insertRowsAtIndexPaths
-deleteRowsAtIndexPaths