Clone App/Twitter

[Twitter Clone] Add tweet actions

밤새는 탐험가89 2024. 5. 24. 20:53

구현 화면

 

 

구현 순서

  • 트윗 액션에 따른 버튼 생성
  • 델리게이트 패턴을 사용하여 각 버튼의 액션 별 전달데이터 설정

 

TweetTableViewCell.swift

  • 트윗 버튼 생성 
  • 델리게이트 패턴 사용 목적으로 프로토콜 선언
  • 데리게이트 패턴을 사용하기 위한 대리자 선언 
  • 각 버튼을 누르면 동작하는 델리게이트 함수 설정
import UIKit


// 데이터 전달 목적으로 델리게이트 패턴 사용 - 프로토콜 선언 - 1
protocol TweetTableViewCellDelegate: AnyObject {
    func tweetTableViewCellDidTapReply()
    func tweetTableViewCellDidTapRetweet()
    func tweetTableViewCellDidTapLike()
    func tweetTableViewCellDidTapShare()
}



class TweetTableViewCell: UITableViewCell {
    
    static let identifier = "TweetTableViewCell"
    
    private let actionSpacing: CGFloat = 60
    
    
    // 대리자 선언 - 2
    weak var delegate: TweetTableViewCellDelegate?
    
    
    private let avatarImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFill
        imageView.layer.cornerRadius = 25
        imageView.layer.masksToBounds = true
        imageView.clipsToBounds = true
        imageView.image = UIImage(systemName: "person")
        imageView.backgroundColor = .green
        return imageView
    }()
    
    
    private let displayNameLabel: UILabel = {
        let label = UILabel()
        label.text = "Explorer"
        label.font = .systemFont(ofSize: 18, weight: .bold)
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    
    private let userNameLabel: UILabel = {
        let label = UILabel()
        label.text = "@Explorer"
        label.textColor = .secondaryLabel
        label.font = .systemFont(ofSize: 12, weight: .regular)
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    
    private let tweetTextContentLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was bo"
        label.numberOfLines = 0
        return label
    }()
    
    
    private let replyButton: UIButton = {
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setImage(UIImage(systemName: "bubble.left"), for: .normal)
        button.tintColor = .gray
        return button
    }()
    
    
    private let retweetButton: UIButton = {
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setImage(UIImage(systemName: "arrow.2.squarepath"), for: .normal)
        button.tintColor = .gray
        return button
    }()
    
    
    private let likeButton: UIButton = {
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setImage(UIImage(systemName: "heart"), for: .normal)
        button.tintColor = .gray
        return button
    }()
    
    
    private let shareButton: UIButton = {
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setImage(UIImage(systemName: "square.and.arrow.up"), for: .normal)
        button.tintColor = .gray
        return button
    }()
    
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        contentView.addSubview(avatarImageView)
        contentView.addSubview(displayNameLabel)
        contentView.addSubview(userNameLabel)
        contentView.addSubview(tweetTextContentLabel)
        
        
        contentView.addSubview(replyButton)
        contentView.addSubview(retweetButton)
        contentView.addSubview(likeButton)
        contentView.addSubview(shareButton)
        
        configureConstraints()
        configureButtons()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
        
    }
    
    
    private func configureConstraints() {
        
        let avatarImageViewConstraints = [
            avatarImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
            avatarImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 14),
            avatarImageView.heightAnchor.constraint(equalToConstant: 50),
            avatarImageView.widthAnchor.constraint(equalToConstant: 50)
        ]
        
        let displayNameLabelConstraints = [
            displayNameLabel.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: 20),
            displayNameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20)
        ]
        
        let userNameLabelConstraints = [
            userNameLabel.leadingAnchor.constraint(equalTo: displayNameLabel.trailingAnchor, constant: 10),
            userNameLabel.centerYAnchor.constraint(equalTo: displayNameLabel.centerYAnchor)
        ]
        
        let tweetTextContentLabelConstraints = [
            tweetTextContentLabel.leadingAnchor.constraint(equalTo: displayNameLabel.leadingAnchor),
            tweetTextContentLabel.topAnchor.constraint(equalTo: displayNameLabel.bottomAnchor, constant: 10),
            tweetTextContentLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
            //tweetTextContentLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
        ]
        
        
        let replyButtonConstraints = [
            replyButton.leadingAnchor.constraint(equalTo: tweetTextContentLabel.leadingAnchor),
            replyButton.topAnchor.constraint(equalTo: tweetTextContentLabel.bottomAnchor, constant: 10),
            replyButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
        ]
        
        let retweetButtonConstraints = [
            retweetButton.leadingAnchor.constraint(equalTo: replyButton.trailingAnchor, constant: actionSpacing),
            retweetButton.centerYAnchor.constraint(equalTo: replyButton.centerYAnchor)
        ]
        
        let likeButtonConstraints = [
            likeButton.leadingAnchor.constraint(equalTo: retweetButton.trailingAnchor, constant: actionSpacing),
            likeButton.centerYAnchor.constraint(equalTo: replyButton.centerYAnchor)
        ]
        
        let shareButtonConstraints = [
            shareButton.leadingAnchor.constraint(equalTo: likeButton.trailingAnchor, constant: actionSpacing),
            shareButton.centerYAnchor.constraint(equalTo: replyButton.centerYAnchor)
        ]
        
        NSLayoutConstraint.activate(avatarImageViewConstraints)
        NSLayoutConstraint.activate(displayNameLabelConstraints)
        NSLayoutConstraint.activate(userNameLabelConstraints)
        NSLayoutConstraint.activate(tweetTextContentLabelConstraints)
        
        NSLayoutConstraint.activate(replyButtonConstraints)
        NSLayoutConstraint.activate(retweetButtonConstraints)
        NSLayoutConstraint.activate(likeButtonConstraints)
        NSLayoutConstraint.activate(shareButtonConstraints)
        
    }
    
    
    // 버튼을 눌렀을 때 동작하는 함수 - 3.
    @objc private func didTapReply() {
        delegate?.tweetTableViewCellDidTapReply()
    }
    
    @objc private func didTapRetweet() {
        delegate?.tweetTableViewCellDidTapRetweet()
    }
    
    @objc private func didTapLike() {
        delegate?.tweetTableViewCellDidTapLike()
    }
    
    @objc private func didTapShare() {
        delegate?.tweetTableViewCellDidTapShare()
    }
    
    private func configureButtons() {
        replyButton.addTarget(self, action: #selector(didTapReply), for: .touchUpInside)
        retweetButton.addTarget(self, action: #selector(didTapRetweet), for: .touchUpInside)
        likeButton.addTarget(self, action: #selector(didTapLike), for: .touchUpInside)
        shareButton.addTarget(self, action: #selector(didTapShare), for: .touchUpInside)
    }
}

 

 

HomeViewController.swift

  • TweetTableViewCellDelegate 프로토콜 준수 
  • 포로토콜 내의 함수 쿠체화
  • cell의 delegate를 HomeViewController로 선언
import UIKit

class HomeViewController: UIViewController {
    
    
    private let timelineTableView: UITableView = {
        let tableView = UITableView()
        tableView.register(TweetTableViewCell.self, forCellReuseIdentifier: TweetTableViewCell.identifier)
        return tableView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(timelineTableView)
        
        timelineTableView.delegate = self
        timelineTableView.dataSource = self
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        timelineTableView.frame = view.bounds
    }
}

extension HomeViewController: UITableViewDelegate, UITableViewDataSource {
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: TweetTableViewCell.identifier, for: indexPath) as? TweetTableViewCell  else { return UITableViewCell()
        }
        
        // 델리게이트 패턴에 대한 대리자를 HomeViewController 설정
        cell.delegate = self
        
        return cell
    }
    
}

extension HomeViewController: TweetTableViewCellDelegate {
    func tweetTableViewCellDidTapReply() {
        print("tweetTableViewCellDidTapReply() - called")
    }
    
    func tweetTableViewCellDidTapRetweet() {
        print("tweetTableViewCellDidTapRetweet() - called")
    }
    
    func tweetTableViewCellDidTapLike() {
        print("tweetTableViewCellDidTapLike() - called")
    }
    
    func tweetTableViewCellDidTapShare() {
        print("tweetTableViewCellDidTapShare() - called")
    }

}

 

 

TIL

  • 어떤 문제가 있었는지?
    • TweetTableViewCell 에서 받은 입력을 HomeViewController를 통해 받아보려고 함
  • 어떻게 해결했는지
    • 델리게이트 패턴을 통해 TweeTableViewCell에서 받은 입력을 처리하기 위한 대리자 역할을 HomeViewController 로 선언함

⭐️ 델리게이트 패턴 설명 ⭐️

 

  • 위의 이미지에서 사각형 내에 버튼을 누르면 기능이 실행되게 한다. 
  • 먼저 프로토콜을 생성한다. 이때 프로토콜 내에는 어떤 기능에 대한 이름 정도만 생성한다. 
  • 예를 들면 레시피 정도? 라고 생각하면 된다. 

 

  • TweeTableViewCellDelegate 프로토콜을 준수하는 delegate 라는 변수 선언한다. 
  • 이때 delegate는 TweetTableViewCell 이라는 음식의 주문서 양식을 생성한다고 생각한다. 

 

  • TweetTableViewCell 내의 버튼에 맞는 함수를 설정한다. 
  • 각 버튼을 누르면 해당 요리 주문서 양식에 맞게 주문을 넣는다고 보면 된다. 

 

  • HomeViewController은 TweetTableViewCellDelegate 프로토콜을 준수하고, 각 함수를 구체화한다. 
  • HomeViewController라는 요리사는 TweetTableViewCellDelegate라는 한식 카테고리 내에 각 요리에 대한 레시피를 작성한다고 생각하면 된다. 

 

⭐️⭐️⭐️ TweetTableViewCell 에서 작성한 주문서를 HomeViewController 라는 요리사가 받았다는 걸 보여줘야 한다. ⭐️⭐️⭐️ 

 

 

https://youtu.be/Kb1Tx5R0fXA?si=Y1_PdGogFrm8OgY6