[ad_1]
I’m implementing carousel with variable merchandise measurement / scale, which is determined by indexPath.
So I began with UICollectionViewFlowLayout subclass which implements required scaling logic, however I am unable to determine why spacing is appropriate solely between heart and subsequent to it objects (left and proper).
Usually I’ve 5 components, heart ingredient (0, 2) has scale issue 1, components (0, 1) and (0, 3) have scale issue 0.888, components (0, 0) and (0, 4) – 0.777.
Spacing between ingredient (0, 2) and components (0, 1) (0, 3) is appropriate and is the same as 10, however spacing between components (0, 0) (0, 1) and (0, 3) (0, 4) is the same as 20.
Right here is the code for UICollectionViewFlowLayout subclass:
open class CarouselEffectFlowLayout: UICollectionViewFlowLayout {
public enum CarouselSpaceMode {
case fastened(spacing: CGFloat)
}
public struct LayoutState {
var measurement: CGSize
var route: UICollectionView.ScrollDirection
func isEqual(_ otherState: LayoutState) -> Bool {
self.measurement.equalTo(otherState.measurement) && self.route == otherState.route
}
}
@IBInspectable open var sideItemScale: CGFloat = 0.888
@IBInspectable open var sideItemAlpha: CGFloat = 0.8
open var spacingMode: CarouselSpaceMode = .fastened(spacing: 10)
personal var state = LayoutState(measurement: CGSize.zero, route: .horizontal)
override open func put together() {
tremendous.put together()
let currentState = LayoutState(measurement: self.collectionView!.bounds.measurement, route: self.scrollDirection)
if !self.state.isEqual(currentState) {
self.setupCollectionView()
self.updateLayout()
self.state = currentState
}
}
personal func setupCollectionView() {
guard let collectionView = self.collectionView else { return }
if collectionView.decelerationRate != UIScrollView.DecelerationRate.quick {
collectionView.decelerationRate = UIScrollView.DecelerationRate.quick
}
}
personal func updateLayout() {
guard let collectionView = self.collectionView else { return }
let collectionSize = collectionView.bounds.measurement
let yInset = (collectionSize.peak - self.itemSize.peak) / 2
let xInset = (collectionSize.width - self.itemSize.width) / 2
self.sectionInset = UIEdgeInsets.init(prime: yInset, left: xInset, backside: yInset, proper: xInset)
let facet = self.itemSize.width
let scaledItemOffset = (facet - facet * self.sideItemScale) / 2
change self.spacingMode {
case .fastened(let spacing):
self.minimumLineSpacing = spacing - scaledItemOffset
}
}
override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
true
}
override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard
let superAttributes = tremendous.layoutAttributesForElements(in: rect),
let attributes = NSArray(array: superAttributes, copyItems: true) as? [UICollectionViewLayoutAttributes]
else { return nil }
return attributes.map({ self.transformLayoutAttributes($0) })
}
personal func transformLayoutAttributes(_ attributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
guard let collectionView = self.collectionView else { return attributes }
let collectionCenter = collectionView.body.measurement.width / 2
let offset = collectionView.contentOffset.x
let normalizedCenter = attributes.heart.x - offset
let maxDistance = self.itemSize.width + self.minimumLineSpacing
let distance = abs(collectionCenter - normalizedCenter)
let ratio = (maxDistance - distance) / maxDistance
let alpha = ratio * (1 - self.sideItemAlpha) + self.sideItemAlpha
let scale = ratio * (1 - self.sideItemScale) + self.sideItemScale
attributes.alpha = alpha
attributes.zIndex = Int(alpha * 10)
attributes.remodel = .init(scaleX: scale, y: scale)
return attributes
}
override open func targetContentOffset(
forProposedContentOffset proposedContentOffset: CGPoint,
withScrollingVelocity velocity: CGPoint
) -> CGPoint {
guard
let collectionView = collectionView,
!collectionView.isPagingEnabled,
let layoutAttributes = self.layoutAttributesForElements(in: collectionView.bounds)
else {
return tremendous.targetContentOffset(forProposedContentOffset: proposedContentOffset)
}
let midSide = collectionView.bounds.measurement.width / 2
let proposedContentOffsetCenterOrigin = proposedContentOffset.x + midSide
var targetContentOffset: CGPoint
let closest = layoutAttributes.sorted {
abs($0.heart.x - proposedContentOffsetCenterOrigin) < abs($1.heart.x - proposedContentOffsetCenterOrigin)
}.first ?? UICollectionViewLayoutAttributes()
targetContentOffset = CGPoint(
x: flooring(closest.heart.x - midSide),
y: proposedContentOffset.y
)
return targetContentOffset
}
}
[ad_2]
