mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-30 11:18:39 +09:00
558 lines
22 KiB
Swift
558 lines
22 KiB
Swift
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import Foundation
|
|
import Shared
|
|
import SDWebImage
|
|
import Storage
|
|
|
|
struct TopSiteCellUX {
|
|
static let TitleHeight: CGFloat = 20
|
|
static let TitleBackgroundColor = UIColor(colorLiteralRed: 1, green: 1, blue: 1, alpha: 0.7)
|
|
static let TitleTextColor = UIColor.black
|
|
static let TitleFont = DynamicFontHelper.defaultHelper.SmallSizeRegularWeightAS
|
|
static let SelectedOverlayColor = UIColor(white: 0.0, alpha: 0.25)
|
|
static let CellCornerRadius: CGFloat = 4
|
|
static let TitleOffset: CGFloat = 5
|
|
static let OverlayColor = UIColor(white: 0.0, alpha: 0.25)
|
|
static let IconSizePercent: CGFloat = 0.8
|
|
static let BorderColor = UIColor(white: 0, alpha: 0.1)
|
|
static let BorderWidth: CGFloat = 0.5
|
|
static let PinIconSize: CGFloat = 12
|
|
static let PinColor = UIColor(rgb: 0x272727)
|
|
}
|
|
|
|
/*
|
|
* The TopSite cell that appears in the ASHorizontalScrollView.
|
|
*/
|
|
class TopSiteItemCell: UICollectionViewCell {
|
|
|
|
var url: URL?
|
|
|
|
lazy var imageView: UIImageView = {
|
|
let imageView = UIImageView()
|
|
imageView.layer.masksToBounds = true
|
|
return imageView
|
|
}()
|
|
|
|
lazy var pinImageView: UIImageView = {
|
|
let imageView = UIImageView()
|
|
imageView.image = UIImage.templateImageNamed("pin_small")
|
|
imageView.tintColor = TopSiteCellUX.PinColor
|
|
return imageView
|
|
}()
|
|
|
|
lazy fileprivate var titleLabel: UILabel = {
|
|
let titleLabel = UILabel()
|
|
titleLabel.layer.masksToBounds = true
|
|
titleLabel.textAlignment = .center
|
|
titleLabel.font = TopSiteCellUX.TitleFont
|
|
titleLabel.textColor = TopSiteCellUX.TitleTextColor
|
|
titleLabel.backgroundColor = UIColor.clear
|
|
return titleLabel
|
|
}()
|
|
|
|
lazy private var faviconBG: UIView = {
|
|
let view = UIView()
|
|
view.layer.cornerRadius = TopSiteCellUX.CellCornerRadius
|
|
view.layer.masksToBounds = true
|
|
view.layer.borderWidth = TopSiteCellUX.BorderWidth
|
|
view.layer.borderColor = TopSiteCellUX.BorderColor.cgColor
|
|
return view
|
|
}()
|
|
|
|
lazy var selectedOverlay: UIView = {
|
|
let selectedOverlay = UIView()
|
|
selectedOverlay.backgroundColor = TopSiteCellUX.OverlayColor
|
|
selectedOverlay.isHidden = true
|
|
return selectedOverlay
|
|
}()
|
|
|
|
lazy var titleBorder: CALayer = {
|
|
let border = CALayer()
|
|
border.backgroundColor = TopSiteCellUX.BorderColor.cgColor
|
|
return border
|
|
}()
|
|
|
|
override var isSelected: Bool {
|
|
didSet {
|
|
self.selectedOverlay.isHidden = !isSelected
|
|
}
|
|
}
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
isAccessibilityElement = true
|
|
accessibilityIdentifier = "TopSite"
|
|
|
|
contentView.addSubview(titleLabel)
|
|
contentView.addSubview(faviconBG)
|
|
contentView.addSubview(imageView)
|
|
contentView.addSubview(selectedOverlay)
|
|
|
|
titleLabel.snp.makeConstraints { make in
|
|
make.left.equalTo(self).offset(TopSiteCellUX.TitleOffset)
|
|
make.right.equalTo(self).offset(-TopSiteCellUX.TitleOffset)
|
|
make.height.equalTo(TopSiteCellUX.TitleHeight)
|
|
make.bottom.equalTo(self)
|
|
}
|
|
|
|
imageView.snp.makeConstraints { make in
|
|
make.size.equalTo(floor(frame.width * TopSiteCellUX.IconSizePercent))
|
|
make.centerX.equalTo(self)
|
|
make.centerY.equalTo(self).inset(-TopSiteCellUX.TitleHeight/2)
|
|
}
|
|
|
|
selectedOverlay.snp.makeConstraints { make in
|
|
make.edges.equalTo(contentView)
|
|
}
|
|
|
|
faviconBG.snp.makeConstraints { make in
|
|
make.top.left.right.equalTo(self)
|
|
make.bottom.equalTo(self).inset(TopSiteCellUX.TitleHeight)
|
|
}
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
titleBorder.frame = CGRect(x: 0, y: frame.height - TopSiteCellUX.TitleHeight - TopSiteCellUX.BorderWidth, width: frame.width, height: TopSiteCellUX.BorderWidth)
|
|
|
|
imageView.snp.remakeConstraints { make in
|
|
make.size.equalTo(floor(self.frame.width * TopSiteCellUX.IconSizePercent))
|
|
make.centerX.equalTo(self)
|
|
make.centerY.equalTo(self).inset(-TopSiteCellUX.TitleHeight/2)
|
|
}
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func prepareForReuse() {
|
|
super.prepareForReuse()
|
|
contentView.backgroundColor = UIColor.clear
|
|
imageView.image = nil
|
|
imageView.backgroundColor = UIColor.clear
|
|
faviconBG.backgroundColor = UIColor.clear
|
|
pinImageView.removeFromSuperview()
|
|
imageView.sd_cancelCurrentImageLoad()
|
|
titleLabel.text = ""
|
|
titleLabel.snp.updateConstraints { make in
|
|
make.left.equalTo(self).offset(TopSiteCellUX.TitleOffset)
|
|
}
|
|
}
|
|
|
|
func configureWithTopSiteItem(_ site: Site) {
|
|
url = site.tileURL
|
|
|
|
if let provider = site.metadata?.providerName {
|
|
titleLabel.text = provider.lowercased()
|
|
} else {
|
|
titleLabel.text = site.tileURL.hostSLD
|
|
}
|
|
|
|
// If its a pinned site add a bullet point to the front
|
|
if let _ = site as? PinnedSite {
|
|
contentView.addSubview(pinImageView)
|
|
pinImageView.snp.makeConstraints { make in
|
|
make.right.equalTo(self.titleLabel.snp.left)
|
|
make.size.equalTo(TopSiteCellUX.PinIconSize)
|
|
make.centerY.equalTo(self.titleLabel.snp.centerY)
|
|
}
|
|
titleLabel.snp.updateConstraints { make in
|
|
make.left.equalTo(self).offset(TopSiteCellUX.PinIconSize)
|
|
}
|
|
}
|
|
|
|
accessibilityLabel = titleLabel.text
|
|
imageView.setFavicon(forSite: site, onCompletion: { [weak self] (color, url) in
|
|
if let url = url, url == self?.url {
|
|
self?.faviconBG.backgroundColor = color
|
|
self?.imageView.backgroundColor = color
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
// An empty cell to show when a row is incomplete
|
|
class EmptyTopsiteDecorationCell: UICollectionReusableView {
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
self.layer.cornerRadius = TopSiteCellUX.CellCornerRadius
|
|
self.layer.borderWidth = TopSiteCellUX.BorderWidth
|
|
self.layer.borderColor = TopSiteCellUX.BorderColor.cgColor
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
}
|
|
|
|
struct ASHorizontalScrollCellUX {
|
|
static let TopSiteCellIdentifier = "TopSiteItemCell"
|
|
static let TopSiteEmptyCellIdentifier = "TopSiteItemEmptyCell"
|
|
|
|
static let TopSiteItemSize = CGSize(width: 75, height: 75)
|
|
static let BackgroundColor = UIColor.white
|
|
static let PageControlRadius: CGFloat = 3
|
|
static let PageControlSize = CGSize(width: 30, height: 15)
|
|
static let PageControlOffset: CGFloat = 12
|
|
static let MinimumInsets: CGFloat = 14
|
|
}
|
|
|
|
/*
|
|
The View that describes the topSite cell that appears in the tableView.
|
|
*/
|
|
class ASHorizontalScrollCell: UICollectionViewCell {
|
|
|
|
lazy var collectionView: UICollectionView = {
|
|
let layout = HorizontalFlowLayout()
|
|
layout.itemSize = ASHorizontalScrollCellUX.TopSiteItemSize
|
|
let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
|
|
collectionView.register(TopSiteItemCell.self, forCellWithReuseIdentifier: ASHorizontalScrollCellUX.TopSiteCellIdentifier)
|
|
collectionView.backgroundColor = UIColor.clear
|
|
collectionView.showsHorizontalScrollIndicator = false
|
|
collectionView.isPagingEnabled = true
|
|
return collectionView
|
|
}()
|
|
|
|
lazy fileprivate var pageControl: FilledPageControl = {
|
|
let pageControl = FilledPageControl()
|
|
pageControl.tintColor = UIColor.gray
|
|
pageControl.indicatorRadius = ASHorizontalScrollCellUX.PageControlRadius
|
|
pageControl.isUserInteractionEnabled = true
|
|
pageControl.isAccessibilityElement = true
|
|
pageControl.accessibilityIdentifier = "pageControl"
|
|
pageControl.accessibilityLabel = Strings.ASPageControlButton
|
|
pageControl.accessibilityTraits = UIAccessibilityTraitButton
|
|
return pageControl
|
|
}()
|
|
|
|
lazy fileprivate var pageControlPress: UITapGestureRecognizer = {
|
|
let press = UITapGestureRecognizer(target: self, action: #selector(ASHorizontalScrollCell.handlePageTap(_:)))
|
|
// press.delegate = self
|
|
return press
|
|
}()
|
|
|
|
weak var delegate: ASHorizontalScrollCellManager? {
|
|
didSet {
|
|
collectionView.delegate = delegate
|
|
collectionView.dataSource = delegate
|
|
delegate?.pageChangedHandler = { [weak self] progress in
|
|
self?.currentPageChanged(progress)
|
|
}
|
|
}
|
|
}
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
isAccessibilityElement = false
|
|
accessibilityIdentifier = "TopSitesCell"
|
|
backgroundColor = UIColor.clear
|
|
contentView.addSubview(collectionView)
|
|
contentView.addSubview(pageControl)
|
|
|
|
pageControl.addGestureRecognizer(self.pageControlPress)
|
|
|
|
collectionView.snp.makeConstraints { make in
|
|
make.edges.equalTo(contentView)
|
|
}
|
|
|
|
pageControl.snp.makeConstraints { make in
|
|
make.size.equalTo(ASHorizontalScrollCellUX.PageControlSize)
|
|
make.top.equalTo(collectionView.snp.bottom).inset(ASHorizontalScrollCellUX.PageControlOffset)
|
|
make.centerX.equalTo(self.snp.centerX)
|
|
}
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
let layout = collectionView.collectionViewLayout as! HorizontalFlowLayout
|
|
|
|
pageControl.pageCount = layout.numberOfPages(with: self.frame.size)
|
|
pageControl.isHidden = pageControl.pageCount <= 1
|
|
}
|
|
|
|
func currentPageChanged(_ currentPage: CGFloat) {
|
|
pageControl.progress = currentPage
|
|
if currentPage == floor(currentPage) {
|
|
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil)
|
|
self.setNeedsLayout()
|
|
}
|
|
}
|
|
|
|
func handlePageTap(_ gesture: UITapGestureRecognizer) {
|
|
guard pageControl.pageCount > 1 else {
|
|
return
|
|
}
|
|
|
|
if pageControl.pageCount > pageControl.currentPage + 1 {
|
|
pageControl.progress = CGFloat(pageControl.currentPage + 1)
|
|
} else {
|
|
pageControl.progress = CGFloat(pageControl.currentPage - 1)
|
|
}
|
|
let swipeCoordinate = CGFloat(pageControl.currentPage) * self.collectionView.frame.size.width
|
|
self.collectionView.setContentOffset(CGPoint(x: swipeCoordinate, y: 0), animated: true)
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
}
|
|
/*
|
|
A custom layout used to show a horizontal scrolling list with paging. Similar to iOS springboard.
|
|
A modified version of http://stackoverflow.com/a/34167915
|
|
*/
|
|
|
|
class HorizontalFlowLayout: UICollectionViewLayout {
|
|
fileprivate var cellCount: Int {
|
|
if let collectionView = collectionView, let dataSource = collectionView.dataSource {
|
|
return dataSource.collectionView(collectionView, numberOfItemsInSection: 0)
|
|
}
|
|
return 0
|
|
}
|
|
var boundsSize = CGSize.zero
|
|
private var insets = UIEdgeInsets(equalInset: ASHorizontalScrollCellUX.MinimumInsets)
|
|
private var sectionInsets: CGFloat = 0
|
|
var itemSize = CGSize.zero
|
|
var cachedAttributes: [UICollectionViewLayoutAttributes]?
|
|
|
|
override func prepare() {
|
|
super.prepare()
|
|
if boundsSize != self.collectionView?.frame.size {
|
|
self.collectionView?.setContentOffset(CGPoint.zero, animated: false)
|
|
}
|
|
boundsSize = self.collectionView?.frame.size ?? CGSize.zero
|
|
cachedAttributes = nil
|
|
register(EmptyTopsiteDecorationCell.self, forDecorationViewOfKind: ASHorizontalScrollCellUX.TopSiteEmptyCellIdentifier)
|
|
}
|
|
|
|
func numberOfPages(with bounds: CGSize) -> Int {
|
|
let itemsPerPage = maxVerticalItemsCount(height: bounds.height) * maxHorizontalItemsCount(width: bounds.width)
|
|
// Sometimes itemsPerPage is 0. In this case just return 0. We dont want to try dividing by 0.
|
|
return itemsPerPage == 0 ? 0 : Int(ceil(Double(cellCount) / Double(itemsPerPage)))
|
|
}
|
|
|
|
func calculateLayout(for size: CGSize) -> (size: CGSize, cellSize: CGSize, cellInsets: UIEdgeInsets) {
|
|
let width = size.width
|
|
let height = size.height
|
|
guard width != 0 else {
|
|
return (size: CGSize.zero, cellSize: self.itemSize, cellInsets: self.insets)
|
|
}
|
|
|
|
let horizontalItemsCount = maxHorizontalItemsCount(width: width)
|
|
var verticalItemsCount = maxVerticalItemsCount(height: height)
|
|
if cellCount <= horizontalItemsCount {
|
|
// If we have only a few items don't provide space for multiple rows.
|
|
verticalItemsCount = 1
|
|
}
|
|
|
|
// Take the number of cells and subtract its space in the view from the height. The left over space is the white space.
|
|
// The left over space is then devided evenly into (n + 1) parts to figure out how much space should be inbetween a cell
|
|
var verticalInsets = floor((height - (CGFloat(verticalItemsCount) * itemSize.height)) / CGFloat(verticalItemsCount + 1))
|
|
var horizontalInsets = floor((width - (CGFloat(horizontalItemsCount) * itemSize.width)) / CGFloat(horizontalItemsCount + 1))
|
|
|
|
// We want a minimum inset to make things not look crowded. We also don't want uneven spacing.
|
|
// If we dont have this. Set a minimum inset and recalculate the size of a cell
|
|
var estimatedItemSize = itemSize
|
|
if horizontalInsets != ASHorizontalScrollCellUX.MinimumInsets {
|
|
verticalInsets = ASHorizontalScrollCellUX.MinimumInsets
|
|
horizontalInsets = ASHorizontalScrollCellUX.MinimumInsets
|
|
estimatedItemSize.width = floor((width - (CGFloat(horizontalItemsCount + 1) * horizontalInsets)) / CGFloat(horizontalItemsCount))
|
|
estimatedItemSize.height = estimatedItemSize.width + TopSiteCellUX.TitleHeight
|
|
}
|
|
|
|
//calculate our estimates.
|
|
let estimatedHeight = floor(estimatedItemSize.height * CGFloat(verticalItemsCount)) + (verticalInsets * (CGFloat(verticalItemsCount) + 1))
|
|
let estimatedSize = CGSize(width: CGFloat(numberOfPages(with: boundsSize)) * width, height: estimatedHeight)
|
|
|
|
let estimatedInsets = UIEdgeInsets(top: verticalInsets, left: horizontalInsets, bottom: verticalInsets, right: horizontalInsets)
|
|
return (size: estimatedSize, cellSize: estimatedItemSize, cellInsets: estimatedInsets)
|
|
}
|
|
|
|
override var collectionViewContentSize: CGSize {
|
|
let estimatedLayout = calculateLayout(for: boundsSize)
|
|
insets = estimatedLayout.cellInsets
|
|
itemSize = estimatedLayout.cellSize
|
|
boundsSize.height = estimatedLayout.size.height
|
|
return estimatedLayout.size
|
|
}
|
|
|
|
func maxVerticalItemsCount(height: CGFloat) -> Int {
|
|
let verticalItemsCount = Int(floor(height / (ASHorizontalScrollCellUX.TopSiteItemSize.height + insets.top)))
|
|
if let delegate = self.collectionView?.delegate as? ASHorizontalLayoutDelegate {
|
|
return delegate.numberOfVerticalItems()
|
|
} else {
|
|
return verticalItemsCount
|
|
}
|
|
}
|
|
|
|
func maxHorizontalItemsCount(width: CGFloat) -> Int {
|
|
let horizontalItemsCount = Int(floor(width / (ASHorizontalScrollCellUX.TopSiteItemSize.width + insets.left)))
|
|
if let delegate = self.collectionView?.delegate as? ASHorizontalLayoutDelegate {
|
|
return delegate.numberOfHorizontalItems()
|
|
} else {
|
|
return horizontalItemsCount
|
|
}
|
|
}
|
|
|
|
override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
|
let decorationAttr = UICollectionViewLayoutAttributes(forDecorationViewOfKind: elementKind, with: indexPath)
|
|
let cellAttr = self.computeLayoutAttributesForCellAtIndexPath(indexPath)
|
|
decorationAttr.frame = cellAttr.frame
|
|
|
|
decorationAttr.frame.size.height -= TopSiteCellUX.TitleHeight
|
|
decorationAttr.zIndex = -1
|
|
return decorationAttr
|
|
}
|
|
|
|
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
|
|
if cachedAttributes != nil {
|
|
return cachedAttributes
|
|
}
|
|
var allAttributes = [UICollectionViewLayoutAttributes]()
|
|
for i in 0 ..< cellCount {
|
|
let indexPath = IndexPath(row: i, section: 0)
|
|
let attr = self.computeLayoutAttributesForCellAtIndexPath(indexPath)
|
|
allAttributes.append(attr)
|
|
}
|
|
|
|
//create decoration attributes
|
|
let horizontalItemsCount = maxHorizontalItemsCount(width: boundsSize.width)
|
|
var numberOfCells = cellCount
|
|
while numberOfCells % horizontalItemsCount != 0 {
|
|
//we need some empty cells dawg.
|
|
|
|
let attr = self.layoutAttributesForDecorationView(ofKind: ASHorizontalScrollCellUX.TopSiteEmptyCellIdentifier, at: IndexPath(item: numberOfCells, section: 0))
|
|
allAttributes.append(attr!)
|
|
numberOfCells += 1
|
|
}
|
|
cachedAttributes = allAttributes
|
|
return allAttributes
|
|
}
|
|
|
|
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
|
return self.computeLayoutAttributesForCellAtIndexPath(indexPath)
|
|
}
|
|
|
|
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
|
|
cachedAttributes = nil
|
|
// Sometimes when the topsiteCell isnt on the screen the newbounds that it tries to layout in is 0
|
|
// Resulting in incorrect layouts. Only layout when a valid width is given
|
|
return newBounds.width > 0
|
|
}
|
|
|
|
func computeLayoutAttributesForCellAtIndexPath(_ indexPath: IndexPath) -> UICollectionViewLayoutAttributes {
|
|
let row = indexPath.row
|
|
let bounds = self.collectionView!.bounds
|
|
|
|
let verticalItemsCount = maxVerticalItemsCount(height: bounds.size.height)
|
|
let horizontalItemsCount = maxHorizontalItemsCount(width: bounds.size.width)
|
|
|
|
let itemsPerPage = verticalItemsCount * horizontalItemsCount
|
|
|
|
let columnPosition = row % horizontalItemsCount
|
|
let rowPosition = (row / horizontalItemsCount) % verticalItemsCount
|
|
let itemPage = Int(floor(Double(row)/Double(itemsPerPage)))
|
|
|
|
let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
|
|
var frame = CGRect.zero
|
|
frame.origin.x = CGFloat(itemPage) * bounds.size.width + CGFloat(columnPosition) * (itemSize.width + insets.left) + insets.left
|
|
frame.origin.y = CGFloat(rowPosition) * (itemSize.height + insets.top) + insets.top
|
|
frame.size = itemSize
|
|
attr.frame = frame
|
|
|
|
return attr
|
|
}
|
|
}
|
|
|
|
/*
|
|
Defines the number of items to show in topsites for different size classes.
|
|
*/
|
|
struct ASTopSiteSourceUX {
|
|
static let verticalItemsForTraitSizes = [UIUserInterfaceSizeClass.compact: 1, UIUserInterfaceSizeClass.regular: 2, UIUserInterfaceSizeClass.unspecified: 0]
|
|
static let maxNumberOfPages = 2
|
|
static let CellIdentifier = "TopSiteItemCell"
|
|
}
|
|
|
|
protocol ASHorizontalLayoutDelegate {
|
|
func numberOfVerticalItems() -> Int
|
|
func numberOfHorizontalItems() -> Int
|
|
}
|
|
|
|
/*
|
|
This Delegate/DataSource is used to manage the ASHorizontalScrollCell's UICollectionView.
|
|
This is left generic enough for it to be re used for other parts of Activity Stream.
|
|
*/
|
|
|
|
class ASHorizontalScrollCellManager: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, ASHorizontalLayoutDelegate {
|
|
|
|
var content: [Site] = []
|
|
|
|
var urlPressedHandler: ((URL, IndexPath) -> Void)?
|
|
var pageChangedHandler: ((CGFloat) -> Void)?
|
|
|
|
// The current traits that define the parent ViewController. Used to determine how many rows/columns should be created.
|
|
var currentTraits: UITraitCollection?
|
|
|
|
// Size classes define how many items to show per row/column.
|
|
func numberOfVerticalItems() -> Int {
|
|
guard let traits = currentTraits else {
|
|
return 0
|
|
}
|
|
return ASTopSiteSourceUX.verticalItemsForTraitSizes[traits.verticalSizeClass]!
|
|
}
|
|
|
|
func numberOfHorizontalItems() -> Int {
|
|
guard let traits = currentTraits else {
|
|
return 0
|
|
}
|
|
let isLandscape = UIInterfaceOrientationIsLandscape(UIApplication.shared.statusBarOrientation)
|
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
|
if isLandscape {
|
|
return 8
|
|
} else {
|
|
return 4
|
|
}
|
|
}
|
|
// On iPad
|
|
// The number of items in a row is equal to the number of highlights in a row * 2
|
|
var numItems: Int = Int(ASPanelUX.numberOfItemsPerRowForSizeClassIpad[traits.horizontalSizeClass])
|
|
if UIInterfaceOrientationIsPortrait(UIApplication.shared.statusBarOrientation) || (traits.horizontalSizeClass == .compact && isLandscape) {
|
|
numItems = numItems - 1
|
|
}
|
|
return numItems * 2
|
|
}
|
|
|
|
func numberOfSections(in collectionView: UICollectionView) -> Int {
|
|
return 1
|
|
}
|
|
|
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
|
return self.content.count
|
|
}
|
|
|
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ASTopSiteSourceUX.CellIdentifier, for: indexPath) as! TopSiteItemCell
|
|
let contentItem = content[indexPath.row]
|
|
cell.configureWithTopSiteItem(contentItem)
|
|
return cell
|
|
}
|
|
|
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
|
let contentItem = content[indexPath.row]
|
|
guard let url = contentItem.url.asURL else {
|
|
return
|
|
}
|
|
urlPressedHandler?(url, indexPath)
|
|
}
|
|
|
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
|
let pageWidth = scrollView.frame.width
|
|
pageChangedHandler?(scrollView.contentOffset.x / pageWidth)
|
|
}
|
|
|
|
}
|