UITableview单元格高度错误

时间:2016-11-07 02:59:33

标签: ios swift uitableview

我正在尝试使用UITableView。基本上,单元格在选中时会扩展,所有其他单元格应该处于原始高度。但不知怎的,他们没有。

Expanded work fine when selecting the non-last cell

Expanded break when selecting the last cell

代码:

@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
        }
    }
}

1 个答案:

答案 0 :(得分:0)

只需使用以下方法就可以做到这一点

-insertRowsAtIndexPaths
-deleteRowsAtIndexPaths