昨天寫下了SlideViewController是怎麼讓他資料刪除的
今天就會是卡片的部分
// 閾值如果超過閾值就進行喜歡不喜歡的判斷,否則卡片回復原位
let theresoldMargin = (UIScreen.main.bounds.size.width/2) * 0.75
let stength : CGFloat = 4
let range : CGFloat = 0.90
// 用來通知上面的ViewController卡片做了什麼事情
protocol TinderCardDelegate: NSObjectProtocol {
func didSelectCard(card: TinderCard)
func cardGoesRight(card: TinderCard)
func cardGoesLeft(card: TinderCard)
func currentCardStatus(card: TinderCard, distance: CGFloat)
func fallbackCard(card: TinderCard)
}
class TinderCard: UIView {
@IBOutlet weak var statusImageView: UIImageView!
@IBOutlet weak var overlayImageView: UIImageView! {
didSet {
overlayImageView.contentMode = .scaleAspectFill
}
}
@IBOutlet weak var overlay: UIImageView! {
didSet {
overlay.contentMode = .scaleAspectFill
}
}
var index: Int!
weak var delegate: TinderCardDelegate?
var xCenter: CGFloat = 0.0
var yCenter: CGFloat = 0.0
var originalPoint = CGPoint.zero
var isLiked = false
var model : Any?
override func awakeFromNib() {
super.awakeFromNib()
}
static func initXib() -> TinderCard {
let v = UINib(nibName: "TinderCard", bundle: nil).instantiate(withOwner: nil, options: nil).first as! TinderCard
return v
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
寫套件的大大使用的是程式直接寫程式碼來處理,但我覺得那樣太麻煩,索性改成使用Xib
也符合這個鐵人賽前面的文章
因為使用的cell的Custom Class,所以不能把initXib放在overrride init內部,否則會造成無盡迴圈,一直Call Function
因為是今天做的更動,所以昨天的程式碼也會有所更動(當然這篇Po上去的時候都是已經改好的狀態)
/*
* Initializing View
*/
func setupView() {
layer.cornerRadius = bounds.width/20
layer.shadowRadius = 3
layer.shadowOpacity = 0.4
layer.shadowOffset = CGSize(width: 0.5, height: 3)
layer.shadowColor = UIColor.darkGray.cgColor
clipsToBounds = true
backgroundColor = .white
originalPoint = center
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.beingDragged))
panGestureRecognizer.delegate = self
addGestureRecognizer(panGestureRecognizer)
}
// 因為我是使用Xib直接畫,而非像作者那樣在外圈導入
func addContentView(element: String){
let url = URL(string: element)!
self.overlay.kf.setImage(with: url)
}
/*
* Card goes right method
*/
func cardGoesRight() {
delegate?.cardGoesRight(card: self)
let finishPoint = CGPoint(x: frame.size.width*2, y: 2 * yCenter + originalPoint.y)
self.removeFromSuperview()
isLiked = true
}
/*
* Card goes left method
*/
func cardGoesLeft() {
delegate?.cardGoesLeft(card: self)
let finishPoint = CGPoint(x: -frame.size.width*2, y: 2 * yCenter + originalPoint.y)
self.removeFromSuperview()
isLiked = false
}
/*
* Setting up initial status for imageviews
*/
fileprivate func setInitialLayoutStatus(isleft:Bool){
statusImageView.alpha = 0.5
overlayImageView.alpha = 0.5
statusImageView.image = makeImage(name: isleft ? "ic_skip" : "ic_like")
overlayImageView.image = makeImage(name: isleft ? "overlay_skip" : "overlay_like")
}
/*
* Acessing image from bundle
*/
fileprivate func makeImage(name: String) -> UIImage? {
let image = UIImage(named: name)
return image
}
/*
* Animation with center point
*/
fileprivate func animateCard(to center:CGPoint, angle:CGFloat = 0, alpha:CGFloat = 0){
self.center = center
self.transform = CGAffineTransform(rotationAngle: angle)
statusImageView.alpha = alpha
overlayImageView.alpha = alpha
}
// MARK: UIGestureRecognizerDelegate Methods
extension TinderCard: UIGestureRecognizerDelegate {
/*
* Gesture methods
*/
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
/*
* Gesture methods
*/
@objc fileprivate func beingDragged(_ gestureRecognizer: UIPanGestureRecognizer) {
xCenter = gestureRecognizer.translation(in: self).x
yCenter = gestureRecognizer.translation(in: self).y
switch gestureRecognizer.state {
// Keep swiping
case .began:
originalPoint = self.center;
self.delegate?.didSelectCard(card: self)
break;
//in the middle of a swipe
case .changed:
let rotationStrength = min(xCenter / UIScreen.main.bounds.size.width, 1)
let rotationAngel = .pi/8 * rotationStrength
let scale = max(1 - abs(rotationStrength) / stength, range)
center = CGPoint(x: originalPoint.x + xCenter, y: originalPoint.y + yCenter)
print("Center",center)
print("OverlayCenter",overlay.center)
print("StatusImageView.Center",statusImageView.center)
print("OverlayImageView",overlayImageView.center)
let transforms = CGAffineTransform(rotationAngle: rotationAngel)
let scaleTransform: CGAffineTransform = transforms.scaledBy(x: scale, y: scale)
self.transform = scaleTransform
updateOverlay(xCenter)
break;
// swipe ended
case .ended:
afterSwipeAction()
break;
case .possible:break
case .cancelled:break
case .failed:break
@unknown default:
fatalError()
}
}
/*
* Tinder Card swipe action
*/
// 這邊是滑動結束後處理喜歡不喜歡,看有沒有超過閾值
fileprivate func afterSwipeAction() {
if xCenter > theresoldMargin {
cardGoesRight()
}
else if xCenter < -theresoldMargin {
cardGoesLeft()
}
else {
self.delegate?.fallbackCard(card: self)
UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1.0, options: [], animations: {
self.center = self.originalPoint
self.transform = CGAffineTransform(rotationAngle: 0)
self.statusImageView.alpha = 0
self.overlayImageView.alpha = 0
})
}
}
/*
* Updating overlay methods
*/
// 更新Mark圖層,可以知道是綠色的還是紅色的
fileprivate func updateOverlay(_ distance: CGFloat) {
statusImageView.image = makeImage(name: distance > 0 ? "ic_like" : "ic_skip")
overlayImageView.image = makeImage(name: distance > 0 ? "overlay_like" : "overlay_skip")
statusImageView.alpha = min(abs(distance) / 100, 0.8)
overlayImageView.alpha = min(abs(distance) / 100, 0.8)
delegate?.currentCardStatus(card: self, distance: distance)
}
}
參考網址:
https://github.com/nickypatson/TinderSwipeView/blob/master/TinderSwipeView/Classes/TinderCard.swift
https://dev.to/ingun37/file-s-owner-is-not-for-uiview-3n9g
不過目前滑動動畫還是有點小Bug
就會滑動到一定位置之後就會開始旋轉,但我還未Debug出來
坑: 動畫好難啊,有看沒有懂,尤其又是搭配手勢