Digital ACT-191 ONEAPP-9311 story: changes about PR notes
This commit is contained in:
parent
7a9910e830
commit
928db0f1fc
@ -78,14 +78,11 @@ open class Carousel: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Aspect-ratio options for tilelet in the carousel. If 'none' is passed, the tilelet will take the height of the tallest item in the carousel.
|
/// views used to render view in the carousel slots.
|
||||||
open var aspectRatio: Tilelet.AspectRatio = .none { didSet { setNeedsUpdate() } }
|
open var views: [UIView] = [] { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Data used to render tilelets in the carousel.
|
|
||||||
open var data: [Any] = [] { didSet { setNeedsUpdate() } }
|
|
||||||
|
|
||||||
/// If provided, width of slots will be rendered based on this value. If omitted, default widths are rendered.
|
/// If provided, width of slots will be rendered based on this value. If omitted, default widths are rendered.
|
||||||
open var width : Width? {
|
open var width: Width? {
|
||||||
get { _width }
|
get { _width }
|
||||||
set {
|
set {
|
||||||
if let newValue {
|
if let newValue {
|
||||||
@ -110,19 +107,11 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile.
|
/// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile.
|
||||||
open var gutter: Gutter {
|
open var gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX { didSet { setNeedsUpdate() } }
|
||||||
get { return _gutter }
|
|
||||||
set {
|
|
||||||
_gutter = newValue
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The amount of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile.
|
/// The amount of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile.
|
||||||
open var layout: CarouselScrollbar.Layout {
|
open var layout: CarouselScrollbar.Layout = UIDevice.isIPad ? .threeUP : .oneUP {
|
||||||
get { return _layout }
|
didSet {
|
||||||
set {
|
|
||||||
_layout = newValue
|
|
||||||
carouselScrollBar.position = 0
|
carouselScrollBar.position = 0
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
@ -143,82 +132,29 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Config object for pagination.
|
/// Config object for pagination.
|
||||||
open var pagination: CarouselPaginationModel {
|
open var pagination: CarouselPaginationModel = .init(kind: .lowContrast, floating: true) { didSet {setNeedsUpdate() } }
|
||||||
get { return _pagination }
|
|
||||||
set {
|
|
||||||
_pagination = newValue
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If provided, will determine the conditions to render the pagination arrows.
|
/// If provided, will determine the conditions to render the pagination arrows.
|
||||||
open var paginationDisplay: PaginationDisplay {
|
open var paginationDisplay: PaginationDisplay = .none { didSet {setNeedsUpdate() } }
|
||||||
get { return _paginationDisplay }
|
|
||||||
set {
|
|
||||||
_paginationDisplay = newValue
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values.
|
/// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values.
|
||||||
/// The default value will be 12px in tablet and 8px in mobile. These values are the default in order to avoid overlapping content within the carousel.
|
/// The default value will be 12px in tablet and 8px in mobile. These values are the default in order to avoid overlapping content within the carousel.
|
||||||
open var paginationInset: CGFloat {
|
open var paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X { didSet { updatePaginationInset() } }
|
||||||
get { return _paginationInset }
|
|
||||||
set {
|
|
||||||
_paginationInset = newValue
|
|
||||||
updatePaginationInset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices.
|
/// Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices.
|
||||||
open var peek: Peek {
|
open var peek: Peek = .standard { didSet { setNeedsUpdate() } }
|
||||||
get { return _peek }
|
|
||||||
set {
|
|
||||||
_peek = newValue
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The initial visible slide's index in the carousel.
|
/// The initial visible slide's index in the carousel.
|
||||||
open var selectedIndex: Int? { didSet { setNeedsUpdate() } }
|
open var selectedIndex: Int? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// If provided, will set the alignment for slot content when the slots has different heights.
|
/// If provided, will set the alignment for slot content when the slots has different heights.
|
||||||
open var slotAlignment: CarouselSlotAlignmentModel? {
|
open var slotAlignment: CarouselSlotAlignmentModel? = nil { didSet { setNeedsUpdate() } }
|
||||||
get { return _slotAlignment }
|
|
||||||
set {
|
|
||||||
if let newValue {
|
|
||||||
_slotAlignment = newValue
|
|
||||||
} else {
|
|
||||||
_slotAlignment = nil
|
|
||||||
}
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render item style. If provided, the slot gets the background, width, height, border-radius.
|
/// Render item style. If provided, the slot gets the background, width, height, border-radius.
|
||||||
open var renderItemStyle: CarouselRenderItemStyle? {
|
open var renderItemStyle: CarouselRenderItemStyle? = nil { didSet { setNeedsUpdate() } }
|
||||||
get { return _renderItemStyle }
|
|
||||||
set {
|
|
||||||
if let newValue {
|
|
||||||
_renderItemStyle = newValue
|
|
||||||
} else {
|
|
||||||
_renderItemStyle = nil
|
|
||||||
}
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render item. It passes a data array object and expects the styled component to apply in return.
|
// /// Render item. It passes a data array object and expects the styled component to apply in return.
|
||||||
open var renderItem: CarouselSlotItemModel? {
|
// open var renderItem: CarouselSlotItemModel? = nil { didSet { setNeedsUpdate() } }
|
||||||
get { _renderItem }
|
|
||||||
set {
|
|
||||||
if let newValue {
|
|
||||||
_renderItem = newValue
|
|
||||||
} else {
|
|
||||||
_renderItem = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
@ -278,18 +214,6 @@ open class Carousel: View {
|
|||||||
/// A publisher for when the carousel moves. Passes parameters (data).
|
/// A publisher for when the carousel moves. Passes parameters (data).
|
||||||
open var onScrollPublisher = PassthroughSubject<Array<Any>, Never>()
|
open var onScrollPublisher = PassthroughSubject<Array<Any>, Never>()
|
||||||
private var onScrollCancellable: AnyCancellable?
|
private var onScrollCancellable: AnyCancellable?
|
||||||
|
|
||||||
internal var _layout: CarouselScrollbar.Layout = UIDevice.isIPad ? .threeUP : .oneUP
|
|
||||||
internal var _pagination: CarouselPaginationModel = .init(kind: .lowContrast, floating: true)
|
|
||||||
internal var _paginationDisplay: PaginationDisplay = .none
|
|
||||||
internal var _paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X
|
|
||||||
internal var _gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX
|
|
||||||
internal var _peek: Peek = .standard
|
|
||||||
internal var _numberOfSlides: Int = 1
|
|
||||||
internal var _slotAlignment: CarouselSlotAlignmentModel? = nil
|
|
||||||
internal var _renderItemStyle: CarouselRenderItemStyle? = nil
|
|
||||||
internal var _renderItem: CarouselSlotItemModel? = nil
|
|
||||||
|
|
||||||
private var _width: Width? = nil
|
private var _width: Width? = nil
|
||||||
private var selectedGroupIndex: Int? = nil
|
private var selectedGroupIndex: Int? = nil
|
||||||
private var containerStackHeightConstraint: NSLayoutConstraint?
|
private var containerStackHeightConstraint: NSLayoutConstraint?
|
||||||
@ -301,7 +225,7 @@ open class Carousel: View {
|
|||||||
// The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile.
|
// The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile.
|
||||||
let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X
|
let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X
|
||||||
|
|
||||||
var slotHeight = 100.0
|
var slotHeight = 50.0
|
||||||
var peekMinimum = 24.0
|
var peekMinimum = 24.0
|
||||||
var minimumSlotWidth = 0.0
|
var minimumSlotWidth = 0.0
|
||||||
var carouselScrollbarMinWidth = 96.0
|
var carouselScrollbarMinWidth = 96.0
|
||||||
@ -386,8 +310,8 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
carouselScrollBar.numberOfSlides = data.count
|
carouselScrollBar.numberOfSlides = views.count
|
||||||
carouselScrollBar.layout = _layout
|
carouselScrollBar.layout = layout
|
||||||
if (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) {
|
if (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) {
|
||||||
carouselScrollBar.position = 1
|
carouselScrollBar.position = 1
|
||||||
}
|
}
|
||||||
@ -417,7 +341,6 @@ open class Carousel: View {
|
|||||||
open override func reset() {
|
open override func reset() {
|
||||||
super.reset()
|
super.reset()
|
||||||
shouldUpdateView = false
|
shouldUpdateView = false
|
||||||
aspectRatio = .none
|
|
||||||
layout = UIDevice.isIPad ? .threeUP : .oneUP
|
layout = UIDevice.isIPad ? .threeUP : .oneUP
|
||||||
pagination = .init(kind: .lowContrast, floating: true)
|
pagination = .init(kind: .lowContrast, floating: true)
|
||||||
paginationDisplay = .none
|
paginationDisplay = .none
|
||||||
@ -473,7 +396,7 @@ open class Carousel: View {
|
|||||||
|
|
||||||
// Show/Hide pagination buttons of Carousel based on First or Middle or Last
|
// Show/Hide pagination buttons of Carousel based on First or Middle or Last
|
||||||
private func showPaginationControls() {
|
private func showPaginationControls() {
|
||||||
if carouselScrollBar.numberOfSlides == _layout.value {
|
if carouselScrollBar.numberOfSlides == layout.value {
|
||||||
previousButton.isHidden = true
|
previousButton.isHidden = true
|
||||||
nextButton.isHidden = true
|
nextButton.isHidden = true
|
||||||
} else {
|
} else {
|
||||||
@ -482,6 +405,26 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func estimateHeightFor(item: CarouselSlotItemModel, with width: CGFloat) -> CGFloat {
|
||||||
|
let itemWidth = width
|
||||||
|
let maxSize = CGSize(width: itemWidth, height: CGFloat.greatestFiniteMagnitude)
|
||||||
|
let estItemSize = item.component?.systemLayoutSizeFitting(maxSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) ?? CGSize(width: itemWidth, height: item.defaultHeight)
|
||||||
|
return estItemSize.height
|
||||||
|
}
|
||||||
|
|
||||||
|
private func fetchCarouselHeight() {
|
||||||
|
if views.count > 0 {
|
||||||
|
var height = slotHeight
|
||||||
|
for x in 0...views.count - 1 {
|
||||||
|
// Add received component
|
||||||
|
let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: views[x])
|
||||||
|
slotHeight = estimateHeightFor(item: item, with: minimumSlotWidth)
|
||||||
|
height = slotHeight > height ? slotHeight : height
|
||||||
|
}
|
||||||
|
slotHeight = height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add carousel slots and load data if any
|
// Add carousel slots and load data if any
|
||||||
private func addCarouselSlots() {
|
private func addCarouselSlots() {
|
||||||
getSlotWidth()
|
getSlotWidth()
|
||||||
@ -495,10 +438,12 @@ open class Carousel: View {
|
|||||||
subView.removeFromSuperview()
|
subView.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchCarouselHeight()
|
||||||
|
|
||||||
// Add carousel items
|
// Add carousel items
|
||||||
if data.count > 0 {
|
if views.count > 0 {
|
||||||
var xPos = 0.0
|
var xPos = 0.0
|
||||||
for x in 0...data.count - 1 {
|
for x in 0...views.count - 1 {
|
||||||
|
|
||||||
// Add Carousel Slot
|
// Add Carousel Slot
|
||||||
let carouselSlot = View().with {
|
let carouselSlot = View().with {
|
||||||
@ -507,8 +452,7 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
scrollView.addSubview(carouselSlot)
|
scrollView.addSubview(carouselSlot)
|
||||||
scrollView.delegate = self
|
scrollView.delegate = self
|
||||||
let size = ratioSize(for: minimumSlotWidth)
|
|
||||||
slotHeight = size.height
|
|
||||||
carouselSlot
|
carouselSlot
|
||||||
.pinTop()
|
.pinTop()
|
||||||
.pinBottom()
|
.pinBottom()
|
||||||
@ -518,24 +462,28 @@ open class Carousel: View {
|
|||||||
carouselSlot.layer.cornerRadius = 12.0
|
carouselSlot.layer.cornerRadius = 12.0
|
||||||
xPos = xPos + minimumSlotWidth + gutter.value
|
xPos = xPos + minimumSlotWidth + gutter.value
|
||||||
|
|
||||||
|
// Add received component
|
||||||
|
let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: views[x])
|
||||||
|
let contentViewHeight = estimateHeightFor(item: item, with: minimumSlotWidth)
|
||||||
|
|
||||||
// Add subview for content to Carousel Slot
|
// Add subview for content to Carousel Slot
|
||||||
let contentView = View().with {
|
let contentView = View().with {
|
||||||
$0.clipsToBounds = true
|
$0.clipsToBounds = true
|
||||||
$0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1)
|
|
||||||
}
|
}
|
||||||
carouselSlot.addSubview(contentView)
|
carouselSlot.addSubview(contentView)
|
||||||
|
|
||||||
// Add received component
|
|
||||||
let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: data[x] as? UIView)
|
|
||||||
if let component = item.component {
|
if let component = item.component {
|
||||||
if slotAlignment != nil {
|
if slotAlignment != nil {
|
||||||
|
// If slotAlignment exist, should use expected height
|
||||||
|
contentView.widthAnchor.constraint(equalToConstant: minimumSlotWidth).activate()
|
||||||
|
contentView.heightAnchor.constraint(equalToConstant: contentViewHeight).activate()
|
||||||
setSlotAlignment(contentView: contentView, parentView: carouselSlot)
|
setSlotAlignment(contentView: contentView, parentView: carouselSlot)
|
||||||
} else {
|
} else {
|
||||||
|
// // If no slotAlignment, should use full slot
|
||||||
contentView.pinToSuperView()
|
contentView.pinToSuperView()
|
||||||
}
|
}
|
||||||
contentView.addSubview(component)
|
contentView.addSubview(component)
|
||||||
component.pinToSuperView()
|
component.pinToSuperView()
|
||||||
contentView.layer.cornerRadius = component.layer.cornerRadius
|
|
||||||
if var surfacedView = component as? Surfaceable {
|
if var surfacedView = component as? Surfaceable {
|
||||||
surfacedView.surface = surface
|
surfacedView.surface = surface
|
||||||
}
|
}
|
||||||
@ -590,7 +538,7 @@ open class Carousel: View {
|
|||||||
// Get the slot width relative to the peak
|
// Get the slot width relative to the peak
|
||||||
private func getSlotWidth() {
|
private func getSlotWidth() {
|
||||||
let actualWidth = containerView.frame.size.width
|
let actualWidth = containerView.frame.size.width
|
||||||
let isScrollbarSuppressed = data.count > 0 && layout.value == data.count
|
let isScrollbarSuppressed = views.count > 0 && layout.value == views.count
|
||||||
let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum
|
let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum
|
||||||
let isPeekNone: Bool = peek == .none
|
let isPeekNone: Bool = peek == .none
|
||||||
minimumSlotWidth = isScrollbarSuppressed || isPeekMinimumOnTablet || isPeekNone ? actualWidth - ((CGFloat(layout.value)-1) * gutter.value): actualWidth - (CGFloat(layout.value) * gutter.value)
|
minimumSlotWidth = isScrollbarSuppressed || isPeekMinimumOnTablet || isPeekNone ? actualWidth - ((CGFloat(layout.value)-1) * gutter.value): actualWidth - (CGFloat(layout.value) * gutter.value)
|
||||||
@ -626,37 +574,6 @@ open class Carousel: View {
|
|||||||
updateScrollPosition(position: carouselScrollBar.position, callbackText:"pageControlClicks")
|
updateScrollPosition(position: carouselScrollBar.position, callbackText:"pageControlClicks")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The size of slot depends on the selected aspect ratio
|
|
||||||
private func ratioSize(for width: CGFloat) -> CGSize {
|
|
||||||
var height: CGFloat = width
|
|
||||||
|
|
||||||
switch aspectRatio {
|
|
||||||
case .ratio1x1:
|
|
||||||
break;
|
|
||||||
case .ratio3x4:
|
|
||||||
height = (4 / 3) * width
|
|
||||||
case .ratio4x3:
|
|
||||||
height = (3 / 4) * width
|
|
||||||
case .ratio2x3:
|
|
||||||
height = (3 / 2) * width
|
|
||||||
case .ratio3x2:
|
|
||||||
height = (2 / 3) * width
|
|
||||||
case .ratio9x16:
|
|
||||||
height = (16 / 9) * width
|
|
||||||
case .ratio16x9:
|
|
||||||
height = (9 / 16) * width
|
|
||||||
case .ratio1x2:
|
|
||||||
height = (2 / 1) * width
|
|
||||||
case .ratio2x1:
|
|
||||||
height = (1 / 2) * width
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return CGSize(width: width, height: height)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updatePaginationInset() {
|
private func updatePaginationInset() {
|
||||||
prevButtonLeadingConstraint?.isActive = false
|
prevButtonLeadingConstraint?.isActive = false
|
||||||
nextButtonTrailingConstraint?.isActive = false
|
nextButtonTrailingConstraint?.isActive = false
|
||||||
@ -688,7 +605,7 @@ open class Carousel: View {
|
|||||||
} else if position == totalPositions {
|
} else if position == totalPositions {
|
||||||
xPos = scrollContentSizeWidth - containerView.frame.size.width
|
xPos = scrollContentSizeWidth - containerView.frame.size.width
|
||||||
} else {
|
} else {
|
||||||
let isScrollbarSuppressed = data.count > 0 && layout.value == data.count
|
let isScrollbarSuppressed = views.count > 0 && layout.value == views.count
|
||||||
let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum
|
let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum
|
||||||
if !isScrollbarSuppressed {
|
if !isScrollbarSuppressed {
|
||||||
let subpart = minimumSlotWidth + gutter.value
|
let subpart = minimumSlotWidth + gutter.value
|
||||||
@ -719,7 +636,7 @@ open class Carousel: View {
|
|||||||
|
|
||||||
// Get the overall positions of the carousel scrollbar relative to the slides and selected layout
|
// Get the overall positions of the carousel scrollbar relative to the slides and selected layout
|
||||||
private func totalPositions() -> Int {
|
private func totalPositions() -> Int {
|
||||||
return Int (ceil (Double(carouselScrollBar.numberOfSlides) / Double(_layout.value)))
|
return Int (ceil (Double(carouselScrollBar.numberOfSlides) / Double(layout.value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,8 @@ public struct CarouselSlotItemModel {
|
|||||||
/// Style props if provided any
|
/// Style props if provided any
|
||||||
public var style: CarouselRenderItemStyle?
|
public var style: CarouselRenderItemStyle?
|
||||||
|
|
||||||
|
public let defaultHeight: CGFloat = 50.0
|
||||||
|
|
||||||
/// Component to be show on Carousel slot
|
/// Component to be show on Carousel slot
|
||||||
public var component: UIView?
|
public var component: UIView?
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user