From 270346601182591daf79bfce7ece61cfc2807deb Mon Sep 17 00:00:00 2001 From: zhenwu <779071985@qq.com> Date: Wed, 25 Jun 2025 15:34:14 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix=EF=BC=9A=E8=AE=BE=E7=BD=AE=E9=80=89?= =?UTF-8?q?=E4=B8=AD=E4=B8=8E=E9=9D=9E=E9=80=89=E4=B8=AD=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E9=A1=B5=E7=A0=81=E7=9A=84path=E4=B8=8D=E7=9B=B8=E5=90=8C?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E4=BF=9D=E6=8C=81=E6=AF=8F=E4=B8=AA=E9=A1=B5?= =?UTF-8?q?=E7=A0=81=E7=9A=84=E9=97=B4=E9=9A=94=E7=9B=B8=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/FSPageControl.swift | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/Sources/FSPageControl.swift b/Sources/FSPageControl.swift index e3bd745..2c0a32b 100644 --- a/Sources/FSPageControl.swift +++ b/Sources/FSPageControl.swift @@ -103,31 +103,41 @@ open class FSPageControl: UIControl { open override func layoutSublayers(of layer: CALayer) { super.layoutSublayers(of: layer) - let diameter = self.itemSpacing let spacing = self.interitemSpacing + + let contentWidth: CGFloat = { + var width: CGFloat = 0.0 + for layer in self.indicatorLayers { + width += layer.path?.boundingBoxOfPath.width ?? diameter + } + width += CGFloat((self.indicatorLayers.count - 1)) * spacing + return width + }() + var x: CGFloat = { switch self.contentHorizontalAlignment { case .left, .leading: return 0 case .center, .fill: let midX = self.contentView.bounds.midX - let amplitude = CGFloat(self.numberOfPages/2) * diameter + spacing*CGFloat((self.numberOfPages-1)/2) - return midX - amplitude + return midX - contentWidth * 0.5 case .right, .trailing: - let contentWidth = diameter*CGFloat(self.numberOfPages) + CGFloat(self.numberOfPages-1)*spacing return contentView.frame.width - contentWidth default: return 0 } }() + for (index,value) in self.indicatorLayers.enumerated() { + let width = value.path?.boundingBoxOfPath.width ?? diameter + let height = value.path?.boundingBoxOfPath.height ?? diameter let state: UIControl.State = (index == self.currentPage) ? .selected : .normal let image = self.images[state] - let size = image?.size ?? CGSize(width: diameter, height: diameter) - let origin = CGPoint(x: x - (size.width-diameter)*0.5, y: self.contentView.bounds.midY-size.height*0.5) + let size = image?.size ?? CGSize(width: width, height: height) + let origin = CGPoint(x: x - (size.width-width)*0.5, y: self.contentView.bounds.midY-size.height*0.5) value.frame = CGRect(origin: origin, size: size) - x = x + spacing + diameter + x = x + spacing + width } } @@ -221,6 +231,8 @@ open class FSPageControl: UIControl { self.setNeedsLayout() DispatchQueue.main.async { self.updateIndicatorsIfNecessary() + // fix:iphone11 pro v17.5.1,更新indicators后没有调用layoutSublayers + self.setNeedsLayout() } } From a7b897e4532e2ea6547d33ff624e1a0f59366e94 Mon Sep 17 00:00:00 2001 From: zhenwu <779071985@qq.com> Date: Wed, 25 Jun 2025 15:37:22 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat=EF=BC=9A=E5=BD=93=E4=B8=80=E9=A1=B5?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=A4=9A=E4=B8=AAitem=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E8=B0=83=E6=95=B4item=E7=9A=84=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E3=80=82=E5=A6=82=EF=BC=9A=E5=BD=93=E4=B8=80=E9=A1=B5=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E4=B8=A4=E4=B8=AAitem=E6=97=B6=EF=BC=8C=E5=85=B6?= =?UTF-8?q?=E4=B8=AD=E4=B8=80=E4=B8=AAitem=E4=BC=9A=E5=B1=85=E4=B8=AD?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=EF=BC=8C=E5=B7=A6=E5=8F=B3=E4=B8=A4=E8=BE=B9?= =?UTF-8?q?=E5=90=84=E6=98=BE=E7=A4=BA=E5=8D=8A=E4=B8=AAitem=EF=BC=8C?= =?UTF-8?q?=E4=BD=86=E6=9C=89=E6=97=B6=E5=80=99=E4=B8=8D=E5=B8=8C=E6=9C=9B?= =?UTF-8?q?=E8=BF=99=E7=A7=8D=E8=B0=83=E6=95=B4=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BasicExampleViewController.swift | 7 ++- Sources/FSPageViewLayout.swift | 21 +++++++-- Sources/FSPagerView.swift | 43 ++++++++++++++++++- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/FSPageViewExample-Swift/FSPagerViewExample/BasicExampleViewController.swift b/FSPageViewExample-Swift/FSPagerViewExample/BasicExampleViewController.swift index 57c806b..2761b9c 100644 --- a/FSPageViewExample-Swift/FSPagerViewExample/BasicExampleViewController.swift +++ b/FSPageViewExample-Swift/FSPagerViewExample/BasicExampleViewController.swift @@ -11,7 +11,7 @@ import UIKit class BasicExampleViewController: UIViewController,UITableViewDataSource,UITableViewDelegate,FSPagerViewDataSource,FSPagerViewDelegate { fileprivate let sectionTitles = ["Configurations", "Decelaration Distance", "Item Size", "Interitem Spacing", "Number Of Items"] - fileprivate let configurationTitles = ["Automatic sliding","Infinite"] + fileprivate let configurationTitles = ["Automatic sliding","Infinite","itemAdjust"] fileprivate let decelerationDistanceOptions = ["Automatic", "1", "2"] fileprivate let imageNames = ["1.jpg","2.jpg","3.jpg","4.jpg","5.jpg","6.jpg","7.jpg"] fileprivate var numberOfItems = 7 @@ -63,6 +63,9 @@ class BasicExampleViewController: UIViewController,UITableViewDataSource,UITable } else if indexPath.row == 1 { // IsInfinite cell.accessoryType = self.pagerView.isInfinite ? .checkmark : .none + } else if indexPath.row == 2 { + // isItemAdjust + cell.accessoryType = self.pagerView.isItemAdjust ? .checkmark : .none } return cell case 1: @@ -130,6 +133,8 @@ class BasicExampleViewController: UIViewController,UITableViewDataSource,UITable self.pagerView.automaticSlidingInterval = 3.0 - self.pagerView.automaticSlidingInterval } else if indexPath.row == 1 { // IsInfinite self.pagerView.isInfinite = !self.pagerView.isInfinite + } else if indexPath.row == 2 { + self.pagerView.isItemAdjust = !self.pagerView.isItemAdjust } tableView.reloadSections([indexPath.section], with: .automatic) case 1: diff --git a/Sources/FSPageViewLayout.swift b/Sources/FSPageViewLayout.swift index 9477ac4..832e31d 100644 --- a/Sources/FSPageViewLayout.swift +++ b/Sources/FSPageViewLayout.swift @@ -14,6 +14,17 @@ class FSPagerViewLayout: UICollectionViewLayout { internal var leadingSpacing: CGFloat = 0 internal var itemSpacing: CGFloat = 0 internal var needsReprepare = true + internal var isItemAdjust: Bool = true { + didSet { + guard let collectionView = collectionView else { return } + if (isItemAdjust) { + self.leadingSpacing = self.scrollDirection == .horizontal ? (collectionView.frame.width-self.actualItemSize.width)*0.5 : (collectionView.frame.height-self.actualItemSize.height)*0.5 + } else { + self.leadingSpacing = 0 + } + self.adjustCollectionViewBounds() + } + } internal var scrollDirection: FSPagerView.ScrollDirection = .horizontal open override class var layoutAttributesClass: AnyClass { @@ -75,7 +86,11 @@ class FSPagerViewLayout: UICollectionViewLayout { return pagerView.interitemSpacing }() self.scrollDirection = pagerView.scrollDirection - self.leadingSpacing = self.scrollDirection == .horizontal ? (collectionView.frame.width-self.actualItemSize.width)*0.5 : (collectionView.frame.height-self.actualItemSize.height)*0.5 + if (isItemAdjust) { + self.leadingSpacing = self.scrollDirection == .horizontal ? (collectionView.frame.width-self.actualItemSize.width)*0.5 : (collectionView.frame.height-self.actualItemSize.height)*0.5 + } else { + self.leadingSpacing = 0 + } self.itemSpacing = (self.scrollDirection == .horizontal ? self.actualItemSize.width : self.actualItemSize.height) + self.actualInteritemSpacing // Calculate and cache contentSize, rather than calculating each time @@ -212,14 +227,14 @@ class FSPagerViewLayout: UICollectionViewLayout { if self.scrollDirection == .vertical { return 0 } - let contentOffsetX = origin.x - (collectionView.frame.width*0.5-self.actualItemSize.width*0.5) + let contentOffsetX = isItemAdjust ? (origin.x - (collectionView.frame.width*0.5-self.actualItemSize.width*0.5)) : origin.x return contentOffsetX }() let contentOffsetY: CGFloat = { if self.scrollDirection == .horizontal { return 0 } - let contentOffsetY = origin.y - (collectionView.frame.height*0.5-self.actualItemSize.height*0.5) + let contentOffsetY = isItemAdjust ? (origin.y - (collectionView.frame.height*0.5-self.actualItemSize.height*0.5)) : origin.y return contentOffsetY }() let contentOffset = CGPoint(x: contentOffsetX, y: contentOffsetY) diff --git a/Sources/FSPagerView.swift b/Sources/FSPagerView.swift index 7fb17d9..68903e8 100644 --- a/Sources/FSPagerView.swift +++ b/Sources/FSPagerView.swift @@ -129,6 +129,15 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega } } + // 在banner中显示多项时,true: 调整item位置使其居中显示, false: 不调整 + @IBInspectable + open var isItemAdjust: Bool = true { + didSet { + self.collectionViewLayout.isItemAdjust = isItemAdjust + self.collectionViewLayout.forceInvalidate() + } + } + /// An unsigned integer value that determines the deceleration distance of the pager view, which indicates the number of passing items during the deceleration. When the value of this property is FSPagerView.automaticDistance, the actual 'distance' is automatically calculated according to the scrolling speed of the pager view. Default is 1. @IBInspectable open var decelerationDistance: UInt = 1 @@ -252,6 +261,27 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega } return IndexPath(item: 0, section: 0) } + // 新增 + fileprivate var leftmostIndexPath: IndexPath { + guard self.numberOfItems > 0, self.collectionView.contentSize != .zero else { + return IndexPath(item: 0, section: 0) + } + let sortedIndexPaths = self.collectionView.indexPathsForVisibleItems.sorted { (l, r) -> Bool in + let leftFrame = self.collectionViewLayout.frame(for: l) + let rightFrame = self.collectionViewLayout.frame(for: r) + switch self.scrollDirection { + case .horizontal: + return leftFrame.minX < rightFrame.minX + case .vertical: + return leftFrame.minY < rightFrame.minY + } + } + let indexPath = sortedIndexPaths.first + if let indexPath = indexPath { + return indexPath + } + return IndexPath(item: 0, section: 0) + } fileprivate var isPossiblyRotating: Bool { guard let animationKeys = self.contentView.layer.animationKeys() else { return false @@ -278,6 +308,7 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega self.backgroundView?.frame = self.bounds self.contentView.frame = self.bounds self.collectionView.frame = self.contentView.bounds + self.collectionViewLayout.needsReprepare = true } open override func willMove(toWindow newWindow: UIWindow?) { @@ -477,8 +508,15 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega /// Reloads all of the data for the collection view. @objc(reloadData) open func reloadData() { - self.collectionViewLayout.needsReprepare = true; + self.collectionViewLayout.needsReprepare = true + let indexPath = self.collectionView.indexPathsForVisibleItems.first self.collectionView.reloadData() + + // fix: 解决在reload时卡在一半的问题 + if let indexPath = indexPath { + let offset = self.collectionViewLayout.contentOffset(for: indexPath) + self.collectionView.setContentOffset(offset, animated: false) + } } /// Selects the item at the specified index and optionally scrolls it into view. @@ -561,6 +599,7 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega // UICollectionView let collectionViewLayout = FSPagerViewLayout() + collectionViewLayout.isItemAdjust = isItemAdjust let collectionView = FSPagerCollectionView(frame: CGRect.zero, collectionViewLayout: collectionViewLayout) collectionView.dataSource = self collectionView.delegate = self @@ -585,7 +624,7 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega return } let contentOffset: CGPoint = { - let indexPath = self.centermostIndexPath + let indexPath = isItemAdjust ? self.centermostIndexPath : self.leftmostIndexPath let section = self.numberOfSections > 1 ? (indexPath.section+(indexPath.item+1)/self.numberOfItems) : 0 let item = (indexPath.item+1) % self.numberOfItems return self.collectionViewLayout.contentOffset(for: IndexPath(item: item, section: section))