求指导Tinder式卡片上下滑动显示用户Profile的实现方法
Hey there! Great job getting the left/right swipe working already—let's walk through how to add the up/down swipe to reveal user profiles. Here's a step-by-step approach with code examples and best practices:
1. First: Link Main Cards to Profile Cards
Instead of relying on array indexes to match TinderCards with ProfileDetailTinderCards, add a direct reference to your card class for reliability:
class TinderCard: UIView { // Add this property to link to its corresponding profile card var linkedProfileCard: ProfileDetailTinderCard? }
When initializing your cards, set this reference:
// Example when loading cards let mainCard = TinderCard() let profileCard = ProfileDetailTinderCard() mainCard.linkedProfileCard = profileCard currentLoadedCardsArray.append(mainCard) newDetailTardsArray.append(profileCard)
2. Modify Pan Gesture to Detect Vertical Swipes
Update your existing pan gesture handler to distinguish between horizontal (left/right) and vertical (up/down) swipes. We'll use a threshold to prioritize one direction over the other:
// Add this gesture to your topmost main card let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handleCardPan(_:))) currentLoadedCardsArray.first?.addGestureRecognizer(panGesture) @objc private func handleCardPan(_ gesture: UIPanGestureRecognizer) { guard let mainCard = gesture.view as? TinderCard else { return } let translation = gesture.translation(in: self.view) let velocity = gesture.velocity(in: self.view) // Determine if this is a vertical swipe (prioritize if y-movement is larger than x) let isVerticalSwipe = abs(translation.y) > abs(translation.x) && abs(translation.y) > 40 // 40pt threshold if isVerticalSwipe { handleVerticalSwipe(for: mainCard, translation: translation, velocity: velocity, gestureState: gesture.state) } else { // Keep your existing left/right swipe logic here handleHorizontalSwipe(for: mainCard, translation: translation, velocity: velocity, gestureState: gesture.state) } }
3. Implement Vertical Swipe Logic
Create a dedicated function to handle up/down swipes, including animations for revealing/hiding the profile card:
private func handleVerticalSwipe(for mainCard: TinderCard, translation: CGPoint, velocity: CGPoint, gestureState: UIGestureRecognizer.State) { guard let profileCard = mainCard.linkedProfileCard else { return } switch gestureState { case .changed: // Update main card position in real-time mainCard.center = CGPoint(x: mainCard.center.x, y: mainCard.center.y + translation.y) gesture.setTranslation(.zero, in: self.view) // Add profile card to view if not already present (positioned off-screen initially) if profileCard.superview == nil { view.insertSubview(profileCard, belowSubview: mainCard) profileCard.center = CGPoint(x: view.center.x, y: view.center.y + view.bounds.height) } // Animate profile card to follow the main card (with a slight delay for depth) profileCard.center = CGPoint(x: profileCard.center.x, y: view.center.y + (translation.y * 0.6)) case .ended: let swipeUpComplete = translation.y < -120 || velocity.y < -600 let swipeDownComplete = translation.y > 120 || velocity.y > 600 if swipeUpComplete { // Swipe up: hide main card, reveal profile card UIView.animate(withDuration: 0.35, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.2) { mainCard.center = CGPoint(x: mainCard.center.x, y: mainCard.center.y - self.view.bounds.height) profileCard.center = self.view.center } completion: { _ in mainCard.removeFromSuperview() // Add gesture to profile card for swipe-down to go back let profilePan = UIPanGestureRecognizer(target: self, action: #selector(handleProfileCardPan(_:))) profileCard.addGestureRecognizer(profilePan) } } else if swipeDownComplete { // Swipe down: reset both cards to initial state resetCardPositions(mainCard: mainCard, profileCard: profileCard) } else { // Swipe didn't meet threshold: bounce back resetCardPositions(mainCard: mainCard, profileCard: profileCard) } default: break } } // Helper to reset cards to original positions private func resetCardPositions(mainCard: TinderCard, profileCard: ProfileDetailTinderCard) { UIView.animate(withDuration: 0.3) { mainCard.center = self.view.center profileCard.center = CGPoint(x: self.view.center.x, y: self.view.center.y + self.view.bounds.height) } completion: { _ in profileCard.removeFromSuperview() } }
4. Add Swipe-Down Logic for Profile Cards
Let users swipe the profile card back down to return to the main card:
@objc private func handleProfileCardPan(_ gesture: UIPanGestureRecognizer) { guard let profileCard = gesture.view as? ProfileDetailTinderCard else { return } let translation = gesture.translation(in: self.view) let velocity = gesture.velocity(in: self.view) switch gesture.state { case .changed: profileCard.center = CGPoint(x: profileCard.center.x, y: profileCard.center.y + translation.y) gesture.setTranslation(.zero, in: self.view) case .ended: if translation.y > 100 || velocity.y > 500 { // Swipe down to return to main card UIView.animate(withDuration: 0.35) { profileCard.center = CGPoint(x: self.view.center.x, y: self.view.center.y + self.view.bounds.height) // Re-add the main card to view if let mainCard = profileCard.linkedMainCard { // Add a linkedMainCard property to ProfileDetailTinderCard self.view.addSubview(mainCard) mainCard.center = self.view.center } } completion: { _ in profileCard.removeFromSuperview() } } else { // Bounce back to center if swipe is too weak UIView.animate(withDuration: 0.3) { profileCard.center = self.view.center } } default: break } }
Additional Tips
- Card Reuse: Implement a reuse pool for both
TinderCardandProfileDetailTinderCardto avoid creating too many views and hitting memory limits. - Z-Order Management: Always ensure the active card (main or profile) is at the top of the view hierarchy using
bringSubviewToFront(_:)orinsertSubview(_:belowSubview:). - Feedback: Add haptic feedback when swipes complete to make the interaction feel more tactile (use
UIImpactFeedbackGenerator).
内容的提问来源于stack exchange,提问作者Lalit kumar




