Как построить макет как у GrabRewards

На этой неделе я опубликовал пост «Внедрение гибкого пользовательского интерфейса с помощью UITableView», и меня попросили сделать демонстрацию, например, GrabRewards.

В этом посте рассказывается, как создать GrabRewards с помощью моего knStaticListController. Проверьте это.

Загрузите стартовый проект на GrabRewards.

Основная ветвь является стартовой. Вы можете оформить заказ в ветке completed чтобы увидеть результат.

Мы строим макет, как этот экран

Я не строю весь экран, элементы ниже исключены.

  • Проведите по верхней вкладке (Обзор, Мои награды)
  • Вызов API (только фиктивные данные)
  • Нижняя полоса точек (вместо этого сплошной цвет)

Давайте начнем.

я положил UICollectionView внутри вида, CategoryView. А также CategoryView помещается внутрь knTableCell.

1. Добавить модель

Проанализируйте целевой пользовательский интерфейс, у нас есть коллекция категорий (значок и имя).

struct Category {
    var icon: String?
    var name: String?
    init(icon: String, name: String) {
        self.icon = icon
        self.name = name
    }
}

2. КатегорияЯчейка

Настройка макета и данных для ячейки представления коллекции

let padding: CGFloat = 24
class CategoryCollectionCell: knCollectionCell {
    let iconImageView = UIMaker.makeImageView()
    let nameLabel = UIMaker.makeLabel(alignment: .center)
    
    // 1
    var data: Category? {
        didSet {
            iconImageView.image = UIImage(named: data?.icon ?? "")
            nameLabel.text = data?.name
        }
    }
    
    override func setupView() {
        // 2
        let iconWrapper = UIMaker.makeView(background: .lightGray)
        iconWrapper.addSubviews(views: iconImageView)
        iconImageView.square(edge: 32)
        iconImageView.center(toView: iconWrapper)
        
        addSubviews(views: iconWrapper, nameLabel)

        iconWrapper.setCorner(radius: 36)
        iconWrapper.square()
        
        iconWrapper.horizontal(toView: self, space: padding / 2)
        iconWrapper.top(toView: self, space: padding)
        
        nameLabel.horizontal(toView: self, space: padding / 2)
        nameLabel.verticalSpacing(toView: iconWrapper, space: padding / 2)
    }
}

В функциях инициализации не так много кода, поэтому я поместил их в конец класса. Сосредоточьтесь на макете.

(1)

Я всегда устанавливаю переменную для данных ячейки dataнезависимо от типов данных.
Я хочу получить что-то:

  let something = data.something

вместо

  let something = event.something
  let something_else = photo.something_else
  let other = category.other

ПРИМЕЧАНИЕ

Просто используйте и помните data.

(2)

  • Настройте макет для значка и метки. Значок находится внутри круга с интервалом, поэтому я добавляю значок в вид и добавляю интервал в этот вид.
  • 32, 36 исправлены для этой демонстрации. Вы можете попробовать несколько разных чисел, чтобы понять. Если вы измените их, вам нужно изменить ширину (96 пикселей) ячейки представления коллекции (следующая часть).
  • padding / 2: расстояние от обертки значка/nameLabel к ведущему и замыкающему.

Почему padding / 2?

  • Предположим, что x = заполнение / 2
  • Макет такой
|-x-[item_1]-x-|	|-x-[item_2]-x-|	|-x-[item_3]-x-|
  • Расстояние между item_1 а также item_2 является 2x = padding, равный стандартному интервалу всего макета. => последовательный и более красивый.

3. Вид по категориям

private let viewHeight: CGFloat = 150
class CategoryView: knView {
    var datasource = [Category]() { didSet { collectionView.reloadData() }}
    var collectionView: UICollectionView!
    
    override func setupView() {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .white
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.showsVerticalScrollIndicator = false
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(CategoryCollectionCell.self,
                                forCellWithReuseIdentifier: "CategoryCollectionCell")
        collectionView.contentInset = UIEdgeInsets(left: padding / 2, right: padding / 2)
        addSubviews(views: collectionView)
        collectionView.fill(toView: self)
        collectionView.height(viewHeight)
    }
}

extension CategoryView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return datasource.count }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCollectionCell", for: indexPath) as! CategoryCollectionCell
        cell.data = datasource[indexPath.row]
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 96, height: viewHeight)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0 // remove spacing between 2 cells in UICollectionView
    }
}

4. Вьюконтроллер

Добавлять CategoryView в ячейку и посмотрите, как она отображается в UITableView.

class ViewController: knStaticListController {
    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
    }

    let categoryView = CategoryView()
    
    override func setupView() {
        view.addSubviews(views: tableView)
        tableView.fill(toView: view)
        let categoryCell = knTableCell.wrap(view: categoryView, space: UIEdgeInsets(bottom: 12))
        categoryCell.backgroundColor = .lightGray
        
        datasource = [categoryCell]
        
        categoryView.datasource = [
            Category(icon: "1", name: "All"),
            Category(icon: "2", name: "Limit Edition"),
            Category(icon: "3", name: "Food"),
            Category(icon: "4", name: "Grab"),
            Category(icon: "5", name: "Service"),
            Category(icon: "6", name: "Shopping"),
            Category(icon: "7", name: "Entertainment"),
            Category(icon: "8", name: "Travel"),
        ]
    }
}

Запустите, и вы увидите первую часть макета GrabRewards.

5. Награда

Подобно категории, Reward, RewardCollectionCell, RewardView настраивает автоматический макет для UICollectionView. Код очень похож. Если некоторые коды недостаточно ясны, дайте мне знать в комментариях.

struct Reward {
    var merchantLogoUrl: String?
    var merchantName: String?
    var bannerUrl: String?
    var title: String?
    var point = 0
    
    init(merchantLogo: String, merchantName: String, banner: String, title: String, point: Int) {
        self.merchantLogoUrl = merchantLogo
        self.merchantName = merchantName
        self.bannerUrl = banner
        self.title = title
        self.point = point
    }
}

6. RewardCollectionCell


class RewardCollectionCell: knCollectionCell {
    let bannerImageView = UIMaker.makeImageView(contentMode: .scaleAspectFill)
    let logoImageView = UIMaker.makeImageView()
    let merchantNameLabel = UIMaker.makeLabel(font: UIFont.boldSystemFont(ofSize: 15),
                                              color: .white)
    let titleLabel = UIMaker.makeLabel(font: UIFont.boldSystemFont(ofSize: 15),
                                              color: .black)
    let pointLabel = UIMaker.makeLabel(font: UIFont.systemFont(ofSize: 15),
                                              color: .black)
    
    var data: Reward? {
        didSet {
            bannerImageView.downloadImage(from: data?.bannerUrl)
            logoImageView.downloadImage(from: data?.merchantLogoUrl)
            merchantNameLabel.text = data?.merchantName
            titleLabel.text = data?.title
            let point = data?.point ?? 0
            pointLabel.text = "\(point) points"
        }
    }
    
    override func setupView() {
        addSubviews(views: bannerImageView, logoImageView, merchantNameLabel, titleLabel, pointLabel)
        
        bannerImageView.horizontal(toView: self, leftPadding: 0, rightPadding: -padding / 2)
        bannerImageView.top(toView: self)
        bannerImageView.height(200)
        bannerImageView.setCorner(radius: 7)
        
        logoImageView.bottomLeft(toView: bannerImageView, bottom: -padding / 2, left: padding / 2)
        logoImageView.square(edge: 32)
        logoImageView.setCorner(radius: 7)
        
        merchantNameLabel.leftHorizontalSpacing(toView: logoImageView, space: -padding / 2)
        merchantNameLabel.centerY(toView: logoImageView)
        
        titleLabel.horizontal(toView: bannerImageView)
        titleLabel.verticalSpacing(toView: bannerImageView, space: padding / 2)
        
        pointLabel.left(toView: titleLabel)
        pointLabel.verticalSpacing(toView: titleLabel, space: padding / 2)
        pointLabel.bottom(toView: self, space: -padding)
    }
}

7. НаградаВью

private let viewHeight: CGFloat = 285
class RewardView: knView {
    let titleLabel = UIMaker.makeLabel(font: UIFont.boldSystemFont(ofSize: 20))
    
    var datasource = [Reward]() { didSet { collectionView.reloadData() }}
    var collectionView: UICollectionView!
    
    convenience init(title: String) {
        self.init()
        titleLabel.text = title
    }
    
    override func setupView() {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .white
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.showsVerticalScrollIndicator = false
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(RewardCollectionCell.self,
                                forCellWithReuseIdentifier: "RewardCollectionCell")
        collectionView.contentInset = UIEdgeInsets(left: padding, right: padding / 2)
        addSubviews(views: titleLabel, collectionView)
        
        titleLabel.topLeft(toView: self, top: padding, left: padding)
        
        collectionView.horizontal(toView: self)
        collectionView.verticalSpacing(toView: titleLabel, space: padding / 2)
        collectionView.bottom(toView: self)
        collectionView.height(viewHeight)
        
        backgroundColor = .white
    }
}

extension RewardView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return datasource.count }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RewardCollectionCell", for: indexPath) as! RewardCollectionCell
        cell.data = datasource[indexPath.row]
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: screenWidth / 1.25, height: viewHeight)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
}

8. Обновите ViewController

  • Добавьте это ниже categoryView
let forMeReward = RewardView(title: "Just for you")
let limitRewards = RewardView(title: "Limit rewards")
  • Добавьте это ниже categoryCell
let forMeCell = knTableCell.wrap(view: forMeReward, space: UIEdgeInsets(bottom: 12))
forMeCell.backgroundColor = .lightGray

let limitCell = knTableCell.wrap(view: limitRewards, space: UIEdgeInsets(bottom: 12))
limitCell.backgroundColor = .lightGray
datasource = [categoryCell, forMeCell, limitCell]
  • Добавьте это ниже categoryView.datasource
let rewards = [
            Reward(merchantLogo: " merchantName: "Gong Cha", banner: " title: "Save 15%", point: 750),
            Reward(merchantLogo: " merchantName: "Gong Cha", banner: " title: "Save 15%", point: 750),
            Reward(merchantLogo: " merchantName: "Gong Cha", banner: " title: "Save 15%", point: 750)
        ]
        
forMeReward.datasource = rewards
limitRewards.datasource = rewards

Теперь запустите, и вы увидите, что окончательный макет завершен.

С knStaticListView, вы можете создать большинство пользовательских интерфейсов, которые вы хотите. Поймите это ясно, вы можете создать свой пользовательский интерфейс без особых усилий и с удовольствием.
Дайте мне знать ваше мнение и то, как вы создаете длинный и сложный пользовательский интерфейс.
Загрузите окончательный исходный код на GrabRewardsответвляться completed.

Наслаждайтесь кодированием.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *