Swift实现UITableView自定义跳转栏:滑动字母时依次输出字符
Hey there! Let's tackle why your custom jump bar only keeps spitting out "A" when you slide from A to Z. The root problem here is that you're probably relying on individual button touch events (like touchDown or touchUpInside) for each letter. Those events only fire when the user first taps a button—they don't track the finger as it slides across other buttons.
Here's how to fix it, step by step:
1. Ditch Per-Button Touch Events
Instead of attaching touch handlers to each letter button, we'll track touch movement across the entire jump bar view. This lets us update the active letter as the user's finger moves.
2. Implement Touch Tracking Logic
We can use either a UIPanGestureRecognizer or override the touch methods of a custom UIView for the jump bar. Let's go with a custom view approach for more control:
First, Create a Custom Jump Bar View
This view will handle all touch tracking and notify us when the active letter changes:
class JumpBarView: UIView { // Store your A-Z characters var jumpIndexes: [Character] = [] // Closure to notify when the letter updates var onLetterChanged: ((Character) -> Void)? // Track the last letter to avoid duplicate outputs private var lastActiveLetter: Character? override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) updateActiveLetter(from: touches) } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) updateActiveLetter(from: touches) } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) // Reset the last letter when the user lifts their finger lastActiveLetter = nil } private func updateActiveLetter(from touches: Set<UITouch>) { guard let touch = touches.first else { return } let touchPoint = touch.location(in: self) // Calculate which letter corresponds to the touch position // Assuming vertical layout—adjust for horizontal if needed let letterHeight = bounds.height / CGFloat(jumpIndexes.count) let letterIndex = Int(touchPoint.y / letterHeight) // Make sure we're within valid bounds guard letterIndex >= 0 && letterIndex < jumpIndexes.count else { return } let currentLetter = jumpIndexes[letterIndex] // Only trigger if the letter changed if currentLetter != lastActiveLetter { print(currentLetter) // This will output A, B, C... as you slide onLetterChanged?(currentLetter) lastActiveLetter = currentLetter } } }
Update Your TableViewJumpBar Class
Now integrate this custom view into your existing class, and set up the letter buttons (they're just for display now—touch logic is handled by the parent view):
class TableViewJumpBar { let tableView: UITableView let view: UIView let jumpBar: JumpBarView! private var jumpIndexes: [Character] init(tableView: UITableView, view: UIView) { self.tableView = tableView self.view = view self.jumpIndexes = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ") self.jumpBar = JumpBarView() // Configure the custom jump bar self.jumpBar.jumpIndexes = self.jumpIndexes self.jumpBar.onLetterChanged = { [weak self] letter in // Optional: Add logic here to scroll your tableView to the corresponding section self?.scrollTableView(to: letter) } setupJumpBarLayout() addLetterButtons() } private func setupJumpBarLayout() { jumpBar.translatesAutoresizingMaskIntoConstraints = false view.addSubview(jumpBar) // Adjust these constraints to fit your UI needs NSLayoutConstraint.activate([ jumpBar.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8), jumpBar.centerYAnchor.constraint(equalTo: view.centerYAnchor), jumpBar.widthAnchor.constraint(equalToConstant: 30), jumpBar.heightAnchor.constraint(equalToConstant: 400) ]) } private func addLetterButtons() { let letterHeight = 400 / CGFloat(jumpIndexes.count) for (index, letter) in jumpIndexes.enumerated() { let button = UIButton(type: .system) button.setTitle(String(letter), for: .normal) button.translatesAutoresizingMaskIntoConstraints = false jumpBar.addSubview(button) NSLayoutConstraint.activate([ button.leadingAnchor.constraint(equalTo: jumpBar.leadingAnchor), button.trailingAnchor.constraint(equalTo: jumpBar.trailingAnchor), button.heightAnchor.constraint(equalToConstant: letterHeight), button.topAnchor.constraint(equalTo: jumpBar.topAnchor, constant: CGFloat(index) * letterHeight) ]) // No need for touch events here—we're using the parent view's touch tracking } } // Optional: Add tableView scrolling logic private func scrollTableView(to letter: Character) { // Implement your section lookup and scroll logic here // Example: // if let section = tableView.dataSource?.sectionIndexTitles?.firstIndex(of: String(letter)) { // let indexPath = IndexPath(row: 0, section: section) // tableView.scrollToRow(at: indexPath, at: .top, animated: true) // } } }
Key Notes to Remember
- Avoid Duplicate Outputs: We track
lastActiveLetterto make sure we don't print the same letter over and over as the user's finger slides across a single button. - Boundary Checks: We make sure the touch index is within the valid range of our
jumpIndexesarray to prevent crashes. - Layout Flexibility: If your jump bar is horizontal instead of vertical, just swap the y-coordinate calculation with x-coordinate, and adjust the constraints accordingly.
This setup will now smoothly output each letter as you slide your finger from A to Z across the jump bar!
内容的提问来源于stack exchange,提问作者Bubu

