Showing
3 changed files
with
104 additions
and
67 deletions
No preview for this file type
| ... | @@ -13,107 +13,144 @@ protocol MyRewardsProfileInfoTableViewCellDelegate: AnyObject { | ... | @@ -13,107 +13,144 @@ protocol MyRewardsProfileInfoTableViewCellDelegate: AnyObject { |
| 13 | 13 | ||
| 14 | @objc(MyRewardsProfileInfoTableViewCell) | 14 | @objc(MyRewardsProfileInfoTableViewCell) |
| 15 | public class MyRewardsProfileInfoTableViewCell: UITableViewCell { | 15 | public class MyRewardsProfileInfoTableViewCell: UITableViewCell { |
| 16 | - @IBOutlet weak var tagView1: UIView! | 16 | + |
| 17 | - @IBOutlet weak var tagLabel1: UILabel! | 17 | + // MARK: - IBOutlets |
| 18 | - @IBOutlet weak var tagView2: UIView! | 18 | + |
| 19 | - @IBOutlet weak var tagLabel2: UILabel! | 19 | + /// Bold "Rewards" title label (top-left) |
| 20 | - @IBOutlet weak var profileImage: UIImageView! | 20 | + @IBOutlet weak var titleLabel: UILabel! |
| 21 | + | ||
| 22 | + /// Horizontally scrollable container for tag pills | ||
| 23 | + @IBOutlet weak var tagsScrollView: UIScrollView! | ||
| 24 | + | ||
| 25 | + /// Horizontal stack view inside the scroll view; tag pill views are added here dynamically | ||
| 26 | + @IBOutlet weak var tagsStackView: UIStackView! | ||
| 27 | + | ||
| 28 | + /// Settings / gear button (top-right) | ||
| 21 | @IBOutlet weak var profileButton: UIButton! | 29 | @IBOutlet weak var profileButton: UIButton! |
| 22 | - | 30 | + |
| 31 | + // MARK: - Delegate | ||
| 32 | + | ||
| 23 | weak var delegate: MyRewardsProfileInfoTableViewCellDelegate? | 33 | weak var delegate: MyRewardsProfileInfoTableViewCellDelegate? |
| 24 | - | 34 | + |
| 25 | - // MARK: - Properties | 35 | + // MARK: - Private |
| 36 | + | ||
| 26 | private var profileModel: ProfileModel? | 37 | private var profileModel: ProfileModel? |
| 27 | 38 | ||
| 39 | + // MARK: - Lifecycle | ||
| 40 | + | ||
| 28 | public override func awakeFromNib() { | 41 | public override func awakeFromNib() { |
| 29 | super.awakeFromNib() | 42 | super.awakeFromNib() |
| 30 | - // Initialization code | 43 | + |
| 31 | - | 44 | + selectionStyle = .none |
| 32 | - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil) | 45 | + |
| 33 | - | 46 | + // Title |
| 47 | + titleLabel.font = UIFont(name: "PingLCG-Bold", size: 26) | ||
| 48 | + ?? UIFont.boldSystemFont(ofSize: 26) | ||
| 49 | + titleLabel.textColor = UIColor(rgb: 0x1D2023) | ||
| 50 | + titleLabel.text = "Rewards" | ||
| 51 | + | ||
| 52 | + // Gear button: render image as-is (no tint) so the gray circle style is preserved | ||
| 53 | + let gearImage = UIImage(named: "settings_circle", in: Bundle.frameworkResourceBundle, compatibleWith: nil)? | ||
| 54 | + .withRenderingMode(.alwaysOriginal) | ||
| 55 | + profileButton.setImage(gearImage, for: .normal) | ||
| 56 | + profileButton.setTitle("", for: .normal) | ||
| 34 | profileButton.addTarget(self, action: #selector(profileButtonTapped), for: .touchUpInside) | 57 | profileButton.addTarget(self, action: #selector(profileButtonTapped), for: .touchUpInside) |
| 35 | - | 58 | + |
| 36 | - tagView1.backgroundColor = UIColor(rgb: 0x09914E) | 59 | + // Tags scroll view |
| 37 | - tagView1.layer.cornerRadius = 4.0 | 60 | + tagsScrollView.showsHorizontalScrollIndicator = false |
| 38 | - tagLabel1.font = UIFont(name: "PingLCG-Regular", size: 17) | 61 | + tagsScrollView.showsVerticalScrollIndicator = false |
| 39 | - tagLabel1.textColor = UIColor(rgb: 0xFFFFFF) | 62 | + |
| 40 | - | 63 | + // Tags stack view |
| 41 | - tagView2.backgroundColor = UIColor(rgb: 0xFC9F25) | 64 | + tagsStackView.axis = .horizontal |
| 42 | - tagView2.layer.cornerRadius = 4.0 | 65 | + tagsStackView.spacing = 9 |
| 43 | - tagLabel2.font = UIFont(name: "PingLCG-Regular", size: 17) | 66 | + tagsStackView.alignment = .center |
| 44 | - tagLabel2.textColor = UIColor(rgb: 0xFFFFFF) | 67 | + tagsStackView.distribution = .fill |
| 45 | - | ||
| 46 | } | 68 | } |
| 47 | 69 | ||
| 48 | public override func setSelected(_ selected: Bool, animated: Bool) { | 70 | public override func setSelected(_ selected: Bool, animated: Bool) { |
| 49 | super.setSelected(selected, animated: animated) | 71 | super.setSelected(selected, animated: animated) |
| 72 | + } | ||
| 50 | 73 | ||
| 51 | - // Configure the view for the selected state | 74 | + public override func prepareForReuse() { |
| 75 | + super.prepareForReuse() | ||
| 76 | + clearTags() | ||
| 52 | } | 77 | } |
| 53 | - | 78 | + |
| 79 | + // MARK: - Actions | ||
| 80 | + | ||
| 54 | @objc private func profileButtonTapped() { | 81 | @objc private func profileButtonTapped() { |
| 55 | delegate?.didTapProfileButton() | 82 | delegate?.didTapProfileButton() |
| 56 | } | 83 | } |
| 57 | - | 84 | + |
| 58 | - // MARK: - Configuration Methods | 85 | + // MARK: - Configuration |
| 59 | - | 86 | + |
| 60 | public func configureCell(data: SectionModel?) { | 87 | public func configureCell(data: SectionModel?) { |
| 61 | guard let data = data, | 88 | guard let data = data, |
| 62 | - let itemType = data.itemType, | 89 | + data.sectionType == .myRewardsProfileInfo else { |
| 63 | - itemType == .profile else { | ||
| 64 | configureDefaultState() | 90 | configureDefaultState() |
| 65 | return | 91 | return |
| 66 | } | 92 | } |
| 67 | - | 93 | + |
| 68 | - // Check if we have real profile data | ||
| 69 | if let metadata = data.metadata, | 94 | if let metadata = data.metadata, |
| 70 | let profile = metadata["profile"] as? ProfileModel { | 95 | let profile = metadata["profile"] as? ProfileModel { |
| 71 | - // Configure with real profile data | ||
| 72 | configureCell(profile: profile) | 96 | configureCell(profile: profile) |
| 73 | } else { | 97 | } else { |
| 74 | - // Configure with default state (no profile data) | ||
| 75 | configureDefaultState() | 98 | configureDefaultState() |
| 76 | } | 99 | } |
| 77 | } | 100 | } |
| 78 | - | 101 | + |
| 79 | public func configureCell(profile: ProfileModel) { | 102 | public func configureCell(profile: ProfileModel) { |
| 80 | self.profileModel = profile | 103 | self.profileModel = profile |
| 81 | - | 104 | + populateTags(segments: profile._user_segments) |
| 82 | - // TODO: Configure tag labels with profile data | ||
| 83 | - // We need to determine what data should populate tagView1 and tagView2 | ||
| 84 | - // Possible options from ProfileModel: | ||
| 85 | - // - _user_points (loyalty points) | ||
| 86 | - // - _badge (user badge/tier) | ||
| 87 | - // - _verified (verification status) | ||
| 88 | - // - Custom business logic based on profile data | ||
| 89 | - | ||
| 90 | - // For now, keep tags visible with current static styling | ||
| 91 | - tagView1.isHidden = false | ||
| 92 | - tagView2.isHidden = false | ||
| 93 | - | ||
| 94 | - // Load profile image if available | ||
| 95 | - if !profile._image_url.isEmpty { | ||
| 96 | - loadProfileImage(from: profile._image_url) | ||
| 97 | - } else { | ||
| 98 | - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil) | ||
| 99 | - } | ||
| 100 | } | 105 | } |
| 101 | - | 106 | + |
| 102 | private func configureDefaultState() { | 107 | private func configureDefaultState() { |
| 103 | - // Default state: no profile data available | ||
| 104 | self.profileModel = nil | 108 | self.profileModel = nil |
| 105 | - | 109 | + clearTags() |
| 106 | - // Keep tags visible even in default state | 110 | + } |
| 107 | - tagView1.isHidden = false | 111 | + |
| 108 | - tagView2.isHidden = false | 112 | + // MARK: - Tag Helpers |
| 109 | - | 113 | + |
| 110 | - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil) | 114 | + /// Removes all tag pill views from the stack view. |
| 115 | + private func clearTags() { | ||
| 116 | + tagsStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + /// Creates one pill view per segment string and adds them to the stack view. | ||
| 120 | + private func populateTags(segments: [String]) { | ||
| 121 | + clearTags() | ||
| 122 | + | ||
| 123 | + for segment in segments where !segment.isEmpty { | ||
| 124 | + let pill = makePillView(text: segment) | ||
| 125 | + tagsStackView.addArrangedSubview(pill) | ||
| 126 | + } | ||
| 111 | } | 127 | } |
| 112 | - | ||
| 113 | - private func loadProfileImage(from urlString: String) { | ||
| 114 | - // For now, use default image - can be enhanced later with URL loading | ||
| 115 | - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil) | ||
| 116 | 128 | ||
| 117 | - // TODO: Add dynamic profile picture and tags | 129 | + /// Builds a rounded pill view with the given text. |
| 130 | + private func makePillView(text: String) -> UIView { | ||
| 131 | + let container = UIView() | ||
| 132 | + container.backgroundColor = UIColor(rgb: 0xF1F2F4) | ||
| 133 | + container.layer.cornerRadius = 13 | ||
| 134 | + container.clipsToBounds = true | ||
| 135 | + | ||
| 136 | + let label = UILabel() | ||
| 137 | + label.text = text | ||
| 138 | + label.font = UIFont(name: "PingLCG-Regular", size: 12) | ||
| 139 | + ?? UIFont.systemFont(ofSize: 12) | ||
| 140 | + label.textColor = UIColor(rgb: 0x00111B) | ||
| 141 | + label.numberOfLines = 1 | ||
| 142 | + label.translatesAutoresizingMaskIntoConstraints = false | ||
| 143 | + | ||
| 144 | + container.addSubview(label) | ||
| 145 | + container.translatesAutoresizingMaskIntoConstraints = false | ||
| 146 | + | ||
| 147 | + NSLayoutConstraint.activate([ | ||
| 148 | + label.topAnchor.constraint(equalTo: container.topAnchor, constant: 6), | ||
| 149 | + label.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -6), | ||
| 150 | + label.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 8), | ||
| 151 | + label.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -8) | ||
| 152 | + ]) | ||
| 153 | + | ||
| 154 | + return container | ||
| 118 | } | 155 | } |
| 119 | } | 156 | } | ... | ... |
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment