diff --git a/.swift-version b/.swift-version index 00750ed..cb2b00e 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3 +3.0.1 diff --git a/ASHorizontalScrollView.podspec b/ASHorizontalScrollView.podspec index 4524746..7bf0a9e 100644 --- a/ASHorizontalScrollView.podspec +++ b/ASHorizontalScrollView.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ASHorizontalScrollView" - s.version = "1.4" + s.version = "1.5" s.summary = "App store style horizontal scroll view" s.description = <<-DESC It acts similar to apps sliding behaviours in App store. There are both Objective-C and Swift version available and they perform exactly the same function, please find whichever you like. @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.homepage = "https://github.com/terenceLuffy/AppStoreStyleHorizontalScrollView" s.license = { :type => 'MIT', :text => <<-LICENSE * The MIT License (MIT) - * Copyright (C) 2014-2016 WEIWEI CHEN + * Copyright (C) 2014-2017 WEIWEI CHEN * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * @@ -20,8 +20,8 @@ Pod::Spec.new do |s| s.author = { "Weiwei Chen" => "terenceluffy@gmail.com" } s.platform = :ios, "8.0" s.ios.deployment_target = "8.0" - s.source = { :git => 'https://github.com/terenceLuffy/AppStoreStyleHorizontalScrollView.git', :tag => "1.4"} - s.source_files = 'Sources/*' + s.source = { :git => 'https://github.com/terenceLuffy/AppStoreStyleHorizontalScrollView.git', :tag => "1.5"} + s.source_files = 'Sources/ASHorizontalScrollView.swift' s.frameworks = 'UIKit' s.requires_arc = true end diff --git a/README.md b/README.md index 0790157..20e6b4c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ App store style horizontal scroll view It acts similar to apps sliding behaviours in App store. There are both Objective-C (do not update anymore since v1.3) and Swift version available and they perform exactly the same function, please find whichever you like. -![](https://dl.dropboxusercontent.com/u/91675323/thumbookr1.gif) ![](https://dl.dropboxusercontent.com/u/91675323/thumbookr2.gif) +![](/images/thumbookr1.gif) ![](/images/thumbookr2.gif) Please note that the gif is not from the sample project. @@ -24,7 +24,7 @@ Install using one of the following options: Swift ```ruby - pod 'ASHorizontalScrollView', '~> 1.4' + pod 'ASHorizontalScrollView', '~> 1.5' ``` Objective-C @@ -36,11 +36,11 @@ Install using one of the following options: Swift ```shell - github "terenceLuffy/AppStoreStyleHorizontalScrollView" ~> 1.4 + github "terenceLuffy/AppStoreStyleHorizontalScrollView" ~> 1.5 ``` ### How to use it? -Please check in [here](http://terenceluffy.github.io/how-to-use-ASHorizontalScrollView/) (updated for v1.4, please check sample project for usage) +Please check in [here](http://terenceluffy.github.io/how-to-use-ASHorizontalScrollView/) (updated for v1.5, please check sample project for usage) ### Versions: 1.0: Initial release @@ -53,12 +53,14 @@ Please check in [here](http://terenceluffy.github.io/how-to-use-ASHorizontalScro 1.4: Add custom width when judging whether to scroll to next item; add support to all Apple devices screen size, now you can specified different mini margin, mini appear width and left margin for all sorts of screen sizes. +1.5: Introduce new properties to allow set number of items per screen for multiple screen sizes instead of setting minimum margins, as well as new properties to center subviews when items are not wide enough to use whole screen width + ### iOS Supported Version -It is developed based on UIScrollView and its delegate methods, so it is completely compatible to most iOS versions. But the Swift version requires 8.0 or above. +iOS 8.0 or above. ### Authorization The MIT License (MIT) -Copyright (C) 2014-2016 WEIWEI CHEN +Copyright (C) 2014-2017 WEIWEI CHEN Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/Sources/ASHorizontalScrollView.swift b/Sources/ASHorizontalScrollView.swift index 48ec282..1cef64b 100644 --- a/Sources/ASHorizontalScrollView.swift +++ b/Sources/ASHorizontalScrollView.swift @@ -1,7 +1,7 @@ /* --------------------------------------------------------- * ASHorizontalScrollView.swift * The MIT License (MIT) -* Copyright (C) 2014-2016 WEIWEI CHEN +* Copyright (C) 2014-2017 WEIWEI CHEN * --------------------------------------------------------- * History * Created by WEIWEI CHEN on 14-6-8. @@ -10,6 +10,7 @@ * Edit by WEIWEI CHEN 16-05-17: change C style code to swift 3 format, fix removeItemAtIndex last index crash bug * Edit by WEIWEI CHEN 16-09-15: Change to adapt Swift 3 with Xcode 8, add support to nib, just change the class on nib file to ASHorizontalScrollView * Edit by WEIWEI CHEN 16-12-02: When current item scroll to more than specified width, auto scroll to next item (mimic App Store behaviour which is about 1/3); add support to all apple screen sizes, now you can specified different mini margin, mini appear width and left margin for all sorts of screen sizes. +* Edit by WEIWEI CHEN 17-01-24: Introduce new properties to allow set number of items per screen for multiple screen sizes instead of setting minimum margins, as well as new properties to center subviews when items are not wide enough to use whole screen width * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * @@ -31,6 +32,41 @@ public struct MarginSettings { /// the mini width appear for last item of current screen, set it 0 if you don't want any part of the last item appear on the right public var miniAppearWidthOfLastItem:CGFloat = 20.0 + + /// number of items per screen, it can be integer like 3, that means total 3 items occupy whole screen, 4.5 means total 4 items and one half item show on the right end. Please note that if numberOfItemsPerScreen is more than screen width, the maximum allowed number of items per screen would be calculated by left margin, and last appeared item percentage which is determined by the fractional number of this value + public var numberOfItemsPerScreen:Float = 0 + + public init() { } + + + /// Use this to set margin if arrange type is set by frame + /// + /// - Parameters: + /// - leftMargin: the margin between left border and first item + /// - miniMarginBetweenItems: the mini margin between items, it is the seed to calculate the actual margin which is not less than + /// - miniAppearWidthOfLastItem: the mini width appear for last item of current screen, set it 0 if you don't want any part of the last item appear on the right + public init(leftMargin:CGFloat, miniMarginBetweenItems:CGFloat, miniAppearWidthOfLastItem:CGFloat) { + self.leftMargin = leftMargin + self.miniMarginBetweenItems = miniMarginBetweenItems + self.miniAppearWidthOfLastItem = miniAppearWidthOfLastItem + } + + + /// Use this to set margin if arrange type is set by number per screen + /// + /// - Parameters: + /// - leftMargin: the margin between left border and first item + /// - numberOfItemsPerScreen: number of items per screen, it can be integer like 3, that means total 3 items occupy whole screen, 4.5 means total 4 items and one half item show on the right end. + /// - note: if numberOfItemsPerScreen is more than screen width, the maximum allowed number of items per screen would be calculated by left margin, and last appeared item percentage which is determined by the fractional number of this value + public init(leftMargin:CGFloat, numberOfItemsPerScreen:Float) { + self.leftMargin = leftMargin + self.numberOfItemsPerScreen = (numberOfItemsPerScreen >= 0 ? numberOfItemsPerScreen : 0) + } +} + +public enum ArrangeType { + case byFrame + case byNumber } open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { @@ -44,13 +80,20 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { } } } + + + /// whether to arrange items by frame or by number of items, if set by frame, all margin would be calculated by frame size, otherwise, calculated by number of items per screen + /// - check `numberOfItemsPerScreen` for arranged by number type + open var arrangeType:ArrangeType = .byFrame /// y position of all items open var itemY: CGFloat = 0 /// an array which refer to all added items - open var items: Array = [] + open var items: [UIView] = [] + /// center subviews when items do not occupy whole screen + public var shouldCenterSubViews:Bool = false /// the uniform size of all added items, please set it before adding any items, otherwise, default size will be applied - open var uniformItemSize:CGSize = CGSize(width: 0,height: 0) { + open var uniformItemSize:CGSize = CGSize.zero { didSet{ itemY = (frame.size.height-self.uniformItemSize.height)/2 } @@ -163,6 +206,13 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { } } + /// number of items per screen, it can be integer like 3, that means total 3 items occupy whole screen, 4.5 means total 4 items and one half item show on the right end + open var numberOfItemsPerScreen:Float { + get { + return self.marginSettings.numberOfItemsPerScreen + } + } + // MARK: - view init public override init(frame: CGRect) { super.init(frame: frame) @@ -190,6 +240,50 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { return false } // MARK: - methods + /** + It re-calculate the item margin to fit in current view frame + - note: This must be called after changing any size or margin property of this class to get acurrate margin + - seealso: calculateMarginBetweenItems + */ + open func setItemsMarginOnce() + { + self.itemsMargin = self.calculateMarginBetweenItems(); + } + + /// Calculate the exact margin between items + open func calculateMarginBetweenItems() -> CGFloat + { + if self.arrangeType == .byFrame { + return calculateMarginByFrame() + } + else { + return calculateMarginByNumberPerScreen() + } + } + + /// Calculate the exact margin by frame + open func calculateMarginByFrame() -> CGFloat { + //calculate how many items listed on current screen except the last half appearance one + let numberOfItemForCurrentWidth = floorf(Float((self.frame.size.width-self.leftMarginPx-self.miniAppearPxOfLastItem)/(self.uniformItemSize.width+self.miniMarginPxBetweenItems))) + return (self.frame.size.width-self.leftMarginPx-self.miniAppearPxOfLastItem)/CGFloat(numberOfItemForCurrentWidth) - self.uniformItemSize.width + } + + /// Calculate the exact margin by number of items per screen + open func calculateMarginByNumberPerScreen() -> CGFloat { + let numOfFull = Int(self.numberOfItemsPerScreen) + if numOfFull <= 0 {// if margin is not set for this screen width, use calculation by frame instead + return calculateMarginByFrame() + } + let lastItemPercentage = self.numberOfItemsPerScreen - Float(numOfFull) + var margin = (self.frame.size.width-self.leftMarginPx-self.uniformItemSize.width * CGFloat(lastItemPercentage))/CGFloat(numOfFull) - self.uniformItemSize.width + if margin <= 0 {//in such case, the number per screen width is larger than the screen width, calculate the max allowed number per screen using the left margin, fractional value of numberOfItemsPerScreen and mini margin between items + let numberOfItemForCurrentWidth = floorf(Float((self.frame.size.width-self.leftMarginPx-self.uniformItemSize.width * CGFloat(lastItemPercentage))/(self.uniformItemSize.width+self.miniMarginPxBetweenItems))) + margin = (self.frame.size.width-self.leftMarginPx-self.uniformItemSize.width * CGFloat(lastItemPercentage))/CGFloat(numberOfItemForCurrentWidth) - self.uniformItemSize.width + } + + return margin + } + /** This add a new item into the scrollview @@ -224,25 +318,6 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { } } - /** - It re-calculate the item margin to fit in current view frame - - note: This must be called after changing any size or margin property of this class to get acurrate margin - - seealso: calculateMarginBetweenItems - */ - open func setItemsMarginOnce() - { - self.itemsMargin = self.calculateMarginBetweenItems(); - } - - /// Calculate the exact margin between items - open func calculateMarginBetweenItems() -> CGFloat - { - //calculate how many items listed on current screen except the last half appearance one - let numberOfItemForCurrentWidth = floorf(Float((self.frame.size.width-self.leftMarginPx-self.miniAppearPxOfLastItem)/(self.uniformItemSize.width+self.miniMarginPxBetweenItems))) - //round func is not compatible in 32bit devices but only in 64bit(5s and iPad Air), so I use this stupid way :) - return CGFloat(Int((self.frame.size.width-self.leftMarginPx-self.miniAppearPxOfLastItem)/CGFloat(numberOfItemForCurrentWidth) - self.uniformItemSize.width)); - } - /** It removes the specified item from scrollview @@ -308,6 +383,17 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { public func refreshSubView() { self.setItemsMarginOnce(); + var itemX = self.leftMarginPx + if self.shouldCenterSubViews { + itemX = centerSubviews() + } + else { + itemX = self.reorderSubViews() + } + self.contentSize = CGSize(width: itemX, height: self.frame.size.height) + } + + private func reorderSubViews() -> CGFloat { var itemX = self.leftMarginPx for item in self.items { @@ -315,8 +401,28 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { itemX += item.frame.width + self.itemsMargin } - itemX = itemX - self.itemsMargin + self.leftMarginPx; - self.contentSize = CGSize(width: itemX, height: self.frame.size.height) + return itemX - self.itemsMargin + self.leftMarginPx; + } + + + /// center subviews if all items can not fully occupy whole screen width + /// + /// - Returns: the scroll view content width + public func centerSubviews() -> CGFloat{ + if let itemLastX = self.items.last?.frame.maxX { + if itemLastX + self.leftMarginPx < self.frame.size.width { + let extraGap = (self.frame.size.width - (self.itemsMargin + self.uniformItemSize.width) * CGFloat(self.items.count) + self.itemsMargin - self.leftMarginPx * 2) / 2 + var itemX = self.leftMarginPx + extraGap + for item in self.items + { + item.frame = CGRect(x: itemX, y: item.frame.origin.y, width: item.frame.width, height: item.frame.height) + itemX += item.frame.width + self.itemsMargin + } + return itemX - self.itemsMargin + self.leftMarginPx + extraGap; + } + return self.reorderSubViews() + } + return 0 } // MARK: - ScrollView delegates diff --git a/images/thumbookr1.gif b/images/thumbookr1.gif new file mode 100644 index 0000000..87772fc Binary files /dev/null and b/images/thumbookr1.gif differ diff --git a/images/thumbookr2.gif b/images/thumbookr2.gif new file mode 100644 index 0000000..fb05229 Binary files /dev/null and b/images/thumbookr2.gif differ diff --git a/sampleScorllViewInSwift/sampleScorllViewInSwift/ASHorizontalScrollView.swift b/sampleScorllViewInSwift/sampleScorllViewInSwift/ASHorizontalScrollView.swift index 48ec282..1cef64b 100644 --- a/sampleScorllViewInSwift/sampleScorllViewInSwift/ASHorizontalScrollView.swift +++ b/sampleScorllViewInSwift/sampleScorllViewInSwift/ASHorizontalScrollView.swift @@ -1,7 +1,7 @@ /* --------------------------------------------------------- * ASHorizontalScrollView.swift * The MIT License (MIT) -* Copyright (C) 2014-2016 WEIWEI CHEN +* Copyright (C) 2014-2017 WEIWEI CHEN * --------------------------------------------------------- * History * Created by WEIWEI CHEN on 14-6-8. @@ -10,6 +10,7 @@ * Edit by WEIWEI CHEN 16-05-17: change C style code to swift 3 format, fix removeItemAtIndex last index crash bug * Edit by WEIWEI CHEN 16-09-15: Change to adapt Swift 3 with Xcode 8, add support to nib, just change the class on nib file to ASHorizontalScrollView * Edit by WEIWEI CHEN 16-12-02: When current item scroll to more than specified width, auto scroll to next item (mimic App Store behaviour which is about 1/3); add support to all apple screen sizes, now you can specified different mini margin, mini appear width and left margin for all sorts of screen sizes. +* Edit by WEIWEI CHEN 17-01-24: Introduce new properties to allow set number of items per screen for multiple screen sizes instead of setting minimum margins, as well as new properties to center subviews when items are not wide enough to use whole screen width * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * @@ -31,6 +32,41 @@ public struct MarginSettings { /// the mini width appear for last item of current screen, set it 0 if you don't want any part of the last item appear on the right public var miniAppearWidthOfLastItem:CGFloat = 20.0 + + /// number of items per screen, it can be integer like 3, that means total 3 items occupy whole screen, 4.5 means total 4 items and one half item show on the right end. Please note that if numberOfItemsPerScreen is more than screen width, the maximum allowed number of items per screen would be calculated by left margin, and last appeared item percentage which is determined by the fractional number of this value + public var numberOfItemsPerScreen:Float = 0 + + public init() { } + + + /// Use this to set margin if arrange type is set by frame + /// + /// - Parameters: + /// - leftMargin: the margin between left border and first item + /// - miniMarginBetweenItems: the mini margin between items, it is the seed to calculate the actual margin which is not less than + /// - miniAppearWidthOfLastItem: the mini width appear for last item of current screen, set it 0 if you don't want any part of the last item appear on the right + public init(leftMargin:CGFloat, miniMarginBetweenItems:CGFloat, miniAppearWidthOfLastItem:CGFloat) { + self.leftMargin = leftMargin + self.miniMarginBetweenItems = miniMarginBetweenItems + self.miniAppearWidthOfLastItem = miniAppearWidthOfLastItem + } + + + /// Use this to set margin if arrange type is set by number per screen + /// + /// - Parameters: + /// - leftMargin: the margin between left border and first item + /// - numberOfItemsPerScreen: number of items per screen, it can be integer like 3, that means total 3 items occupy whole screen, 4.5 means total 4 items and one half item show on the right end. + /// - note: if numberOfItemsPerScreen is more than screen width, the maximum allowed number of items per screen would be calculated by left margin, and last appeared item percentage which is determined by the fractional number of this value + public init(leftMargin:CGFloat, numberOfItemsPerScreen:Float) { + self.leftMargin = leftMargin + self.numberOfItemsPerScreen = (numberOfItemsPerScreen >= 0 ? numberOfItemsPerScreen : 0) + } +} + +public enum ArrangeType { + case byFrame + case byNumber } open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { @@ -44,13 +80,20 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { } } } + + + /// whether to arrange items by frame or by number of items, if set by frame, all margin would be calculated by frame size, otherwise, calculated by number of items per screen + /// - check `numberOfItemsPerScreen` for arranged by number type + open var arrangeType:ArrangeType = .byFrame /// y position of all items open var itemY: CGFloat = 0 /// an array which refer to all added items - open var items: Array = [] + open var items: [UIView] = [] + /// center subviews when items do not occupy whole screen + public var shouldCenterSubViews:Bool = false /// the uniform size of all added items, please set it before adding any items, otherwise, default size will be applied - open var uniformItemSize:CGSize = CGSize(width: 0,height: 0) { + open var uniformItemSize:CGSize = CGSize.zero { didSet{ itemY = (frame.size.height-self.uniformItemSize.height)/2 } @@ -163,6 +206,13 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { } } + /// number of items per screen, it can be integer like 3, that means total 3 items occupy whole screen, 4.5 means total 4 items and one half item show on the right end + open var numberOfItemsPerScreen:Float { + get { + return self.marginSettings.numberOfItemsPerScreen + } + } + // MARK: - view init public override init(frame: CGRect) { super.init(frame: frame) @@ -190,6 +240,50 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { return false } // MARK: - methods + /** + It re-calculate the item margin to fit in current view frame + - note: This must be called after changing any size or margin property of this class to get acurrate margin + - seealso: calculateMarginBetweenItems + */ + open func setItemsMarginOnce() + { + self.itemsMargin = self.calculateMarginBetweenItems(); + } + + /// Calculate the exact margin between items + open func calculateMarginBetweenItems() -> CGFloat + { + if self.arrangeType == .byFrame { + return calculateMarginByFrame() + } + else { + return calculateMarginByNumberPerScreen() + } + } + + /// Calculate the exact margin by frame + open func calculateMarginByFrame() -> CGFloat { + //calculate how many items listed on current screen except the last half appearance one + let numberOfItemForCurrentWidth = floorf(Float((self.frame.size.width-self.leftMarginPx-self.miniAppearPxOfLastItem)/(self.uniformItemSize.width+self.miniMarginPxBetweenItems))) + return (self.frame.size.width-self.leftMarginPx-self.miniAppearPxOfLastItem)/CGFloat(numberOfItemForCurrentWidth) - self.uniformItemSize.width + } + + /// Calculate the exact margin by number of items per screen + open func calculateMarginByNumberPerScreen() -> CGFloat { + let numOfFull = Int(self.numberOfItemsPerScreen) + if numOfFull <= 0 {// if margin is not set for this screen width, use calculation by frame instead + return calculateMarginByFrame() + } + let lastItemPercentage = self.numberOfItemsPerScreen - Float(numOfFull) + var margin = (self.frame.size.width-self.leftMarginPx-self.uniformItemSize.width * CGFloat(lastItemPercentage))/CGFloat(numOfFull) - self.uniformItemSize.width + if margin <= 0 {//in such case, the number per screen width is larger than the screen width, calculate the max allowed number per screen using the left margin, fractional value of numberOfItemsPerScreen and mini margin between items + let numberOfItemForCurrentWidth = floorf(Float((self.frame.size.width-self.leftMarginPx-self.uniformItemSize.width * CGFloat(lastItemPercentage))/(self.uniformItemSize.width+self.miniMarginPxBetweenItems))) + margin = (self.frame.size.width-self.leftMarginPx-self.uniformItemSize.width * CGFloat(lastItemPercentage))/CGFloat(numberOfItemForCurrentWidth) - self.uniformItemSize.width + } + + return margin + } + /** This add a new item into the scrollview @@ -224,25 +318,6 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { } } - /** - It re-calculate the item margin to fit in current view frame - - note: This must be called after changing any size or margin property of this class to get acurrate margin - - seealso: calculateMarginBetweenItems - */ - open func setItemsMarginOnce() - { - self.itemsMargin = self.calculateMarginBetweenItems(); - } - - /// Calculate the exact margin between items - open func calculateMarginBetweenItems() -> CGFloat - { - //calculate how many items listed on current screen except the last half appearance one - let numberOfItemForCurrentWidth = floorf(Float((self.frame.size.width-self.leftMarginPx-self.miniAppearPxOfLastItem)/(self.uniformItemSize.width+self.miniMarginPxBetweenItems))) - //round func is not compatible in 32bit devices but only in 64bit(5s and iPad Air), so I use this stupid way :) - return CGFloat(Int((self.frame.size.width-self.leftMarginPx-self.miniAppearPxOfLastItem)/CGFloat(numberOfItemForCurrentWidth) - self.uniformItemSize.width)); - } - /** It removes the specified item from scrollview @@ -308,6 +383,17 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { public func refreshSubView() { self.setItemsMarginOnce(); + var itemX = self.leftMarginPx + if self.shouldCenterSubViews { + itemX = centerSubviews() + } + else { + itemX = self.reorderSubViews() + } + self.contentSize = CGSize(width: itemX, height: self.frame.size.height) + } + + private func reorderSubViews() -> CGFloat { var itemX = self.leftMarginPx for item in self.items { @@ -315,8 +401,28 @@ open class ASHorizontalScrollView: UIScrollView, UIScrollViewDelegate { itemX += item.frame.width + self.itemsMargin } - itemX = itemX - self.itemsMargin + self.leftMarginPx; - self.contentSize = CGSize(width: itemX, height: self.frame.size.height) + return itemX - self.itemsMargin + self.leftMarginPx; + } + + + /// center subviews if all items can not fully occupy whole screen width + /// + /// - Returns: the scroll view content width + public func centerSubviews() -> CGFloat{ + if let itemLastX = self.items.last?.frame.maxX { + if itemLastX + self.leftMarginPx < self.frame.size.width { + let extraGap = (self.frame.size.width - (self.itemsMargin + self.uniformItemSize.width) * CGFloat(self.items.count) + self.itemsMargin - self.leftMarginPx * 2) / 2 + var itemX = self.leftMarginPx + extraGap + for item in self.items + { + item.frame = CGRect(x: itemX, y: item.frame.origin.y, width: item.frame.width, height: item.frame.height) + itemX += item.frame.width + self.itemsMargin + } + return itemX - self.itemsMargin + self.leftMarginPx + extraGap; + } + return self.reorderSubViews() + } + return 0 } // MARK: - ScrollView delegates diff --git a/sampleScorllViewInSwift/sampleScorllViewInSwift/ViewController.swift b/sampleScorllViewInSwift/sampleScorllViewInSwift/ViewController.swift index 8f5c892..1c51210 100644 --- a/sampleScorllViewInSwift/sampleScorllViewInSwift/ViewController.swift +++ b/sampleScorllViewInSwift/sampleScorllViewInSwift/ViewController.swift @@ -8,6 +8,7 @@ * Edit by WEIWEI CHEN 14-9-21: fix problems to work on xcode 6.0.1 * Edit by WEIWEI CHEN 15-12-09: change to adapt Swift 2.1 with Xcode 7 & iOS 9.0 * Edit by WEIWEI CHEN 16-09-15: Change to adapt Swift 3 with Xcode 8 +* Edit by WEIWEI CHEN 17-01-24: Change to use new properties and center items function * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * @@ -60,26 +61,30 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour let horizontalScrollView:ASHorizontalScrollView = ASHorizontalScrollView(frame:CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: kCellHeight)) //for iPhone 5s and lower versions in portrait horizontalScrollView.marginSettings_320 = MarginSettings(leftMargin: 10, miniMarginBetweenItems: 5, miniAppearWidthOfLastItem: 20) + //for iPhone 4s and lower versions in landscape + horizontalScrollView.marginSettings_480 = MarginSettings(leftMargin: 10, miniMarginBetweenItems: 5, miniAppearWidthOfLastItem: 20) // for iPhone 6 plus and 6s plus in portrait horizontalScrollView.marginSettings_414 = MarginSettings(leftMargin: 10, miniMarginBetweenItems: 5, miniAppearWidthOfLastItem: 20) // for iPhone 6 plus and 6s plus in landscape horizontalScrollView.marginSettings_736 = MarginSettings(leftMargin: 10, miniMarginBetweenItems: 10, miniAppearWidthOfLastItem: 30) //for all other screen sizes that doesn't set here, it would use defaultMarginSettings instead + horizontalScrollView.defaultMarginSettings = MarginSettings(leftMargin: 10, miniMarginBetweenItems: 10, miniAppearWidthOfLastItem: 20) if indexPath.row == 0{ //you can set margin for specific item size here + horizontalScrollView.shouldCenterSubViews = true horizontalScrollView.marginSettings_414?.miniMarginBetweenItems = 20 horizontalScrollView.uniformItemSize = CGSize(width: 50, height: 50) //this must be called after changing any size or margin property of this class to get acurrate margin horizontalScrollView.setItemsMarginOnce() - for _ in 1...13{ + for _ in 1...3{ let button = UIButton(frame: CGRect.zero) button.backgroundColor = UIColor.blue horizontalScrollView.addItem(button) } + _ = horizontalScrollView.centerSubviews() } else if indexPath.row == 1 { - horizontalScrollView.marginSettings_414?.miniAppearWidthOfLastItem = 30 horizontalScrollView.uniformItemSize = CGSize(width: 80, height: 50) //this must be called after changing any size or margin property of this class to get acurrate margin horizontalScrollView.setItemsMarginOnce() @@ -89,6 +94,24 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour horizontalScrollView.addItem(button) } } + else if indexPath.row == 2 { + //instead of using frame to determine margin, using number of items per screen to calculate margin maybe eaiser than setting mini margin for multiple screen size + horizontalScrollView.arrangeType = .byNumber + horizontalScrollView.marginSettings_320 = MarginSettings(leftMargin: 10, numberOfItemsPerScreen: 4.25) + horizontalScrollView.marginSettings_480 = MarginSettings(leftMargin: 10, numberOfItemsPerScreen: 5.25) + horizontalScrollView.marginSettings_414 = MarginSettings(leftMargin: 10, numberOfItemsPerScreen: 4.25) + horizontalScrollView.marginSettings_736 = MarginSettings(leftMargin: 10, numberOfItemsPerScreen: 7.375) + //for all the other screen sizes which are not set here, margin would be calculated by frame instead + + horizontalScrollView.uniformItemSize = CGSize(width: 80, height: 50) + //this must be called after changing any size or margin property of this class to get acurrate margin + horizontalScrollView.setItemsMarginOnce() + for _ in 1...20{ + let button = UIButton(frame: CGRect.zero) + button.backgroundColor = UIColor.gray + horizontalScrollView.addItem(button) + } + } cell?.contentView.addSubview(horizontalScrollView) horizontalScrollView.translatesAutoresizingMaskIntoConstraints = false cell?.contentView.addConstraint(NSLayoutConstraint(item: horizontalScrollView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: kCellHeight)) @@ -104,7 +127,7 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 2 + return 3 } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat