Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions ScrollPager.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,17 @@
attributes = {
LastSwiftMigration = 0700;
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0800;
LastUpgradeCheck = 0920;
ORGANIZATIONNAME = "Aryan Ghassemi";
TargetAttributes = {
15F9717A1A9A86E400169B7E = {
CreatedOnToolsVersion = 6.1.1;
DevelopmentTeam = 24FGDP9KJW;
LastSwiftMigration = 0800;
LastSwiftMigration = 0920;
};
15F9718F1A9A86E400169B7E = {
CreatedOnToolsVersion = 6.1.1;
LastSwiftMigration = 0800;
LastSwiftMigration = 0920;
TestTargetID = 15F9717A1A9A86E400169B7E;
};
};
Expand Down Expand Up @@ -280,14 +280,20 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
Expand Down Expand Up @@ -327,14 +333,20 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
Expand Down Expand Up @@ -368,7 +380,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.aryaxt.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_VERSION = 4.0;
};
name = Debug;
};
Expand All @@ -381,7 +394,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.aryaxt.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_VERSION = 4.0;
};
name = Release;
};
Expand All @@ -401,7 +415,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.aryaxt.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScrollPager.app/ScrollPager";
};
name = Debug;
Expand All @@ -418,7 +433,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.aryaxt.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScrollPager.app/ScrollPager";
};
name = Release;
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down Expand Up @@ -40,6 +40,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
Expand Down Expand Up @@ -69,6 +70,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -10,6 +10,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
Expand All @@ -30,6 +31,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
70 changes: 39 additions & 31 deletions ScrollPager/Source/ScrollPager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@
import UIKit

@objc public protocol ScrollPagerDelegate: NSObjectProtocol {
@objc optional func scrollPager(scrollPager: ScrollPager, changedIndex: Int)
@objc optional func scrollPager(_ scrollPager: ScrollPager, changedIndex: Int)
}

@IBDesignable public class ScrollPager: UIView, UIScrollViewDelegate{

private var selectedIndex = 0
public private(set) var selectedIndex = 0
private let indicatorView = UIView()
private var buttons = [UIButton]()
private var views = [UIView]()
Expand Down Expand Up @@ -105,7 +105,7 @@ import UIKit

private func initialize() {
#if TARGET_INTERFACE_BUILDER
addSegmentsWithTitles(["One", "Two", "Three", "Four"])
addSegments(with: ["One", "Two", "Three", "Four"])
#endif
}

Expand All @@ -120,45 +120,52 @@ import UIKit

// MARK: - Public Methods -

public func addSegmentsWithTitlesAndViews(segments: [(title: String, view: UIView)]) {
public func addSegments(with segments: [(title: String, view: UIView)]) {

addButtons(titleOrImages: segments.map { $0.title as AnyObject })
addViews(segmentViews: segments.map { $0.view })
addButtons(segments.map { $0.title as AnyObject })
addViews(segments.map { $0.view })

redrawComponents()
}

public func addSegmentsWithImagesAndViews(segments: [(image: UIImage, view: UIView)]) {
public func addSegments(with segments: [(image: UIImage, view: UIView)]) {

addButtons(titleOrImages: segments.map { $0.image })
addViews(segmentViews: segments.map { $0.view })
addButtons(segments.map { $0.image })
addViews(segments.map { $0.view })

redrawComponents()
}

public func addSegmentsWithTitles(segmentTitles: [String]) {
addButtons(titleOrImages: segmentTitles as [AnyObject])
@objc(addSegmentsWithTitles:)
public func addSegments(with segmentTitles: [String]) {
addButtons(segmentTitles as [AnyObject])
redrawComponents()
}
public func addSegmentsWithImages(segmentImages: [UIImage]) {
addButtons(titleOrImages: segmentImages)
@objc(addSegmentsWithImages:)
public func addSegments(with segmentImages: [UIImage]) {
addButtons(segmentImages)
redrawComponents()
}

public func setSelectedIndex(index: Int, animated: Bool) {
setSelectedIndex(index: index, animated: animated, moveScrollView: true)
public func setSelectedIndex(_ index: Int, animated: Bool) {
setSelectedIndex(index, animated: animated, moveScrollView: true)
}

public func viewForItem(at index: Int) -> UIView? {
guard index < views.count else {
return nil
}
return views[index]
}

// MARK: - Private -

private func setSelectedIndex(index: Int, animated: Bool, moveScrollView: Bool) {
private func setSelectedIndex(_ index: Int, animated: Bool, moveScrollView: Bool) {
selectedIndex = index

moveToIndex(index: index, animated: animated, moveScrollView: moveScrollView)
move(to: index, animated: animated, moveScrollView: moveScrollView)
}

private func addViews(segmentViews: [UIView]) {
private func addViews(_ segmentViews: [UIView]) {
guard let scrollView = scrollView else { fatalError("trying to add views but the scrollView is nil") }

for view in scrollView.subviews {
Expand All @@ -172,7 +179,7 @@ import UIKit
}
}

private func addButtons(titleOrImages: [AnyObject]) {
private func addButtons(_ titleOrImages: [AnyObject]) {
for button in buttons {
button.removeFromSuperview()
}
Expand All @@ -182,7 +189,7 @@ import UIKit
for i in 0..<titleOrImages.count {
let button = UIButton(type: .custom)
button.tag = i
button.addTarget(self, action: #selector(ScrollPager.buttonSelected(sender:)), for: .touchUpInside)
button.addTarget(self, action: #selector(buttonSelected(_:)), for: .touchUpInside)
buttons.append(button)

if let title = titleOrImages[i] as? String {
Expand All @@ -197,7 +204,7 @@ import UIKit
}
}

private func moveToIndex(index: Int, animated: Bool, moveScrollView: Bool) {
private func move(to index: Int, animated: Bool, moveScrollView: Bool) {
animationInProgress = true

UIView.animate(withDuration: animated ? TimeInterval(animationDuration) : 0.0, delay: 0.0, options: .curveEaseOut, animations: { [weak self] in
Expand All @@ -213,7 +220,7 @@ import UIKit
if strongSelf.indicatorSizeMatchesTitle {
guard let string = button.titleLabel?.text else { fatalError("missing title on button, title is required for width calculation") }
guard let font = button.titleLabel?.font else { fatalError("missing dont on button, title is required for width calculation") }
let size = string.size(attributes: [NSFontAttributeName: font])
let size = string.size(withAttributes: [NSAttributedStringKey.font: font])
let x = width * CGFloat(index) + ((width - size.width) / CGFloat(2))
strongSelf.indicatorView.frame = CGRect(x: x, y: indicatorY, width: size.width, height: strongSelf.indicatorHeight)
}
Expand All @@ -237,7 +244,7 @@ import UIKit
redrawButtons()

if buttons.count > 0 {
moveToIndex(index: selectedIndex, animated: false, moveScrollView: false)
move(to: selectedIndex, animated: false, moveScrollView: false)
}

if let scrollView = scrollView {
Expand Down Expand Up @@ -266,14 +273,15 @@ import UIKit
}
}

internal func buttonSelected(sender: UIButton) {
@objc internal func buttonSelected(_ sender: UIButton) {
if sender.tag == selectedIndex {
return
}
// change the selected index
setSelectedIndex(sender.tag, animated: true, moveScrollView: true)
// notify the delegate that index has changed
delegate?.scrollPager?(self, changedIndex: sender.tag)

delegate?.scrollPager?(scrollPager: self, changedIndex: sender.tag)

setSelectedIndex(index: sender.tag, animated: true, moveScrollView: true)
}

// MARK: - UIScrollView Delegate -
Expand All @@ -287,8 +295,8 @@ import UIKit
}

if Int(page) != selectedIndex {
setSelectedIndex(index: Int(page), animated: true, moveScrollView: false)
delegate?.scrollPager?(scrollPager: self, changedIndex: Int(page))
setSelectedIndex(Int(page), animated: true, moveScrollView: false)
delegate?.scrollPager?(self, changedIndex: Int(page))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions ScrollPager/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ class ViewController: UIViewController, ScrollPagerDelegate {
fourthView.textAlignment = .center

scrollPager.delegate = self
scrollPager.addSegmentsWithTitlesAndViews(segments: [
scrollPager.addSegments(with: [
("Home", firstView),
("Public Feed", secondView),
("Profile", thirdView),
("One More", fourthView)
])

secondScrollPager.addSegmentsWithImages(segmentImages: [
secondScrollPager.addSegments(with: [
UIImage(named: "envelope")!,
UIImage(named: "home")!,
UIImage(named: "like")!,
Expand Down