Skip to content

Commit

Permalink
release 1.5
Browse files Browse the repository at this point in the history
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
  • Loading branch information
terenceLuffy committed Jan 24, 2017
1 parent 0d261cd commit 48096d8
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .swift-version
@@ -1 +1 @@
3
3.0.1
8 changes: 4 additions & 4 deletions ASHorizontalScrollView.podspec
@@ -1,14 +1,14 @@
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.
DESC
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:
*
Expand All @@ -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
14 changes: 8 additions & 6 deletions README.md
Expand Up @@ -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.

Expand All @@ -24,7 +24,7 @@ Install using one of the following options:

Swift
```ruby
pod 'ASHorizontalScrollView', '~> 1.4'
pod 'ASHorizontalScrollView', '~> 1.5'
```

Objective-C
Expand All @@ -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
Expand All @@ -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:

Expand Down
154 changes: 130 additions & 24 deletions 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.
Expand All @@ -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:
*
Expand All @@ -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 {
Expand All @@ -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<UIView> = []
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
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -308,15 +383,46 @@ 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
{
item.frame = CGRect(x: itemX, y: item.frame.origin.y, width: item.frame.width, height: item.frame.height)
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
Expand Down
Binary file added images/thumbookr1.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/thumbookr2.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 48096d8

Please sign in to comment.