Commit be896548 by Daniel Dahan

TabMenuController rework for view controller resource optimizations, and…

TabMenuController rework for view controller resource optimizations, and issue-552 fix, to remove @objc on UIKit subclasses TextView and PageTabBarController
parent 3972e840
...@@ -97,7 +97,6 @@ public protocol PageTabBarControllerDelegate { ...@@ -97,7 +97,6 @@ public protocol PageTabBarControllerDelegate {
optional func pageTabBarController(pageTabBarController: PageTabBarController, didTransitionTo viewController: UIViewController) optional func pageTabBarController(pageTabBarController: PageTabBarController, didTransitionTo viewController: UIViewController)
} }
@objc(PageTabBarController)
open class PageTabBarController: RootController { open class PageTabBarController: RootController {
/// Reference to the PageTabBar. /// Reference to the PageTabBar.
@IBInspectable @IBInspectable
......
...@@ -44,6 +44,7 @@ open class TabMenuBarItem: FlatButton { ...@@ -44,6 +44,7 @@ open class TabMenuBarItem: FlatButton {
public enum TabMenuAlignment: Int { public enum TabMenuAlignment: Int {
case top case top
case bottom case bottom
case hidden
} }
extension UIViewController { extension UIViewController {
...@@ -80,8 +81,37 @@ extension UIViewController { ...@@ -80,8 +81,37 @@ extension UIViewController {
open class TabMenuController: UIViewController { open class TabMenuController: UIViewController {
@IBInspectable @IBInspectable
open let tabBar = TabBar() open var selectedIndex: Int {
didSet {
scrollView.setContentOffset(CGPoint(x: scrollView.width * CGFloat(selectedIndex), y: 0), animated: true)
guard false == tabBar?.isAnimating else {
return
}
tabBar?.select(at: selectedIndex)
}
}
/// Enables and disables bouncing when swiping.
open var isBounceEnabled: Bool {
get {
return scrollView.bounces
}
set(value) {
scrollView.bounces = value
}
}
/// The TabBar used to switch between view controllers.
@IBInspectable
open fileprivate(set) var tabBar: TabBar?
/// The UIScrollView used to pan the application pages.
@IBInspectable
open let scrollView = UIScrollView()
/// An Array of UIViewControllers.
open var viewControllers: [UIViewController] { open var viewControllers: [UIViewController] {
didSet { didSet {
oldValue.forEach { oldValue.forEach {
...@@ -95,16 +125,12 @@ open class TabMenuController: UIViewController { ...@@ -95,16 +125,12 @@ open class TabMenuController: UIViewController {
} }
} }
@IBInspectable open var tabMenuAlignment = TabMenuAlignment.bottom {
open var selectedIndex: Int {
didSet { didSet {
scrollView.setContentOffset(CGPoint(x: scrollView.width * CGFloat(selectedIndex), y: 0), animated: true) layoutSubviews()
} }
} }
@IBInspectable
open let scrollView = UIScrollView()
/** /**
An initializer that initializes the object with a NSCoder object. An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance. - Parameter aDecoder: A NSCoder instance.
...@@ -143,6 +169,29 @@ open class TabMenuController: UIViewController { ...@@ -143,6 +169,29 @@ open class TabMenuController: UIViewController {
open func layoutSubviews() { open func layoutSubviews() {
layoutScrollView() layoutScrollView()
layoutViewControllers() layoutViewControllers()
let p = (tabBar?.intrinsicContentSize.height ?? 0) + (tabBar?.layoutEdgeInsets.top ?? 0) + (tabBar?.layoutEdgeInsets.bottom ?? 0)
let y = view.height - p
tabBar?.height = p
tabBar?.width = view.width + (tabBar?.layoutEdgeInsets.left ?? 0) + (tabBar?.layoutEdgeInsets.right ?? 0)
switch tabMenuAlignment {
case .top:
tabBar?.isHidden = false
tabBar?.y = 0
scrollView.y = p
scrollView.height = y
case .bottom:
tabBar?.isHidden = false
tabBar?.y = y
scrollView.y = 0
scrollView.height = y
case .hidden:
tabBar?.isHidden = true
scrollView.y = 0
scrollView.height = view.height
}
} }
/** /**
...@@ -153,18 +202,13 @@ open class TabMenuController: UIViewController { ...@@ -153,18 +202,13 @@ open class TabMenuController: UIViewController {
when subclassing. when subclassing.
*/ */
open func prepare() { open func prepare() {
prepareTabBar()
prepareScrollView() prepareScrollView()
prepareViewControllers() prepareViewControllers()
} }
} }
extension TabMenuController { extension TabMenuController {
fileprivate func prepareTabBar() { /// Prepares the scrollView used to pan through view controllers.
tabBar.isLineAnimated = false
tabBar.lineAlignment = .top
}
fileprivate func prepareScrollView() { fileprivate func prepareScrollView() {
scrollView.delegate = self scrollView.delegate = self
scrollView.bounces = false scrollView.bounces = false
...@@ -174,15 +218,102 @@ extension TabMenuController { ...@@ -174,15 +218,102 @@ extension TabMenuController {
view.addSubview(scrollView) view.addSubview(scrollView)
} }
/// Prepares the view controllers.
fileprivate func prepareViewControllers() { fileprivate func prepareViewControllers() {
let count = 2 < viewControllers.count ? 3 : viewControllers.count
scrollView.contentSize = CGSize(width: scrollView.width * CGFloat(count), height: scrollView.height)
if 0 == selectedIndex {
for i in 0..<count {
let vc = viewControllers[i]
addChildViewController(vc)
vc.didMove(toParentViewController: self)
vc.view.clipsToBounds = true
vc.view.contentScaleFactor = Screen.scale
scrollView.addSubview(vc.view)
}
} else if viewControllers.count - 1 == selectedIndex {
for i in 0..<count {
let vc = viewControllers[count - i - 1]
addChildViewController(vc)
vc.didMove(toParentViewController: self)
vc.view.clipsToBounds = true
vc.view.contentScaleFactor = Screen.scale
scrollView.addSubview(vc.view)
}
} else {
var vc = viewControllers[selectedIndex]
addChildViewController(vc)
vc.didMove(toParentViewController: self)
vc.view.clipsToBounds = true
vc.view.contentScaleFactor = Screen.scale
scrollView.addSubview(vc.view)
vc = viewControllers[selectedIndex - 1]
addChildViewController(vc)
vc.didMove(toParentViewController: self)
vc.view.clipsToBounds = true
vc.view.contentScaleFactor = Screen.scale
scrollView.addSubview(vc.view)
vc = viewControllers[selectedIndex + 1]
addChildViewController(vc)
vc.didMove(toParentViewController: self)
vc.view.clipsToBounds = true
vc.view.contentScaleFactor = Screen.scale
scrollView.addSubview(vc.view)
}
prepareTabBar()
}
/**
Prepares the tabBar buttons.
- Parameter _ buttons: An Array of UIButtons.
*/
fileprivate func prepareTabBarButtons(_ buttons: [UIButton]) {
guard let tb = tabBar else {
return
}
tb.buttons = buttons
for v in tb.buttons {
v.removeTarget(self, action: #selector(tb.handleButton(button:)), for: .touchUpInside)
v.removeTarget(self, action: #selector(handleTabBarButton(button:)), for: .touchUpInside)
v.addTarget(self, action: #selector(handleTabBarButton(button:)), for: .touchUpInside)
}
tb.select(at: selectedIndex)
}
fileprivate func prepareTabBar() {
guard 0 < viewControllers.count else {
return
}
var buttons = [UIButton]()
for v in viewControllers { for v in viewControllers {
addChildViewController(v) let button = v.tabMenuBarItem as UIButton
scrollView.addSubview(v.view) buttons.append(button)
v.didMove(toParentViewController: self)
v.view.clipsToBounds = true
v.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
v.view.contentScaleFactor = Screen.scale
} }
guard 0 < buttons.count else {
tabBar = nil
return
}
guard nil == tabBar else {
prepareTabBarButtons(buttons)
return
}
tabBar = TabBar()
tabBar?.isLineAnimated = false
tabBar?.lineAlignment = .top
view.addSubview(tabBar!)
prepareTabBarButtons(buttons)
} }
} }
...@@ -190,20 +321,96 @@ extension TabMenuController { ...@@ -190,20 +321,96 @@ extension TabMenuController {
fileprivate func layoutScrollView() { fileprivate func layoutScrollView() {
scrollView.frame = view.bounds scrollView.frame = view.bounds
scrollView.contentSize = CGSize(width: scrollView.width * CGFloat(viewControllers.count), height: scrollView.height) scrollView.contentSize = CGSize(width: scrollView.width * CGFloat(viewControllers.count), height: scrollView.height)
print(selectedIndex)
scrollView.contentOffset = CGPoint(x: scrollView.width * CGFloat(selectedIndex), y: 0) scrollView.contentOffset = CGPoint(x: scrollView.width * CGFloat(selectedIndex), y: 0)
} }
fileprivate func layoutViewControllers() { fileprivate func layoutViewControllers() {
for i in 0..<viewControllers.count { let count = 2 < viewControllers.count ? 3 : viewControllers.count
let v = viewControllers[i] scrollView.contentSize = CGSize(width: scrollView.width * CGFloat(count), height: scrollView.height)
v.view.frame = CGRect(x: CGFloat(i) * scrollView.width, y: 0, width: scrollView.width, height: scrollView.height)
if 0 == selectedIndex {
for i in 0..<count {
let vc = viewControllers[i]
vc.view.frame = CGRect(x: CGFloat(i) * scrollView.width, y: 0, width: scrollView.width, height: scrollView.height)
}
} else if viewControllers.count - 1 == selectedIndex {
for i in 0..<count {
let j = count - i - 1
let vc = viewControllers[j]
vc.view.frame = CGRect(x: CGFloat(j) * scrollView.width, y: 0, width: scrollView.width, height: scrollView.height)
}
} else {
var vc = viewControllers[selectedIndex]
vc.view.frame = CGRect(x: scrollView.width, y: 0, width: scrollView.width, height: scrollView.height)
vc = viewControllers[selectedIndex - 1]
vc.view.frame = CGRect(x: 0, y: 0, width: scrollView.width, height: scrollView.height)
vc = viewControllers[selectedIndex + 1]
vc.view.frame = CGRect(x: 2 * scrollView.width, y: 0, width: scrollView.width, height: scrollView.height)
} }
}
}
extension TabMenuController {
/**
Handles the pageTabBarButton.
- Parameter button: A UIButton.
*/
@objc
fileprivate func handleTabBarButton(button: UIButton) {
guard let tb = tabBar else {
return
}
guard let index = tb.buttons.index(of: button) else {
return
}
guard index != selectedIndex else {
return
}
if 1 < abs(index - selectedIndex) {
let last = viewControllers.count - 1
var pos = 0
if last == selectedIndex {
pos = last - 1
} else if 0 == selectedIndex {
pos = 1
} else {
pos = selectedIndex + (index > selectedIndex ? 1 : -1)
}
scrollView.setContentOffset(CGPoint(x: scrollView.width * CGFloat(pos), y: 0), animated: false)
}
selectedIndex = index
} }
} }
extension TabMenuController: UIScrollViewDelegate { extension TabMenuController: UIScrollViewDelegate {
@objc @objc
open func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let tb = tabBar else {
return
}
guard tb.isAnimating else {
return
}
guard let selected = tb.selected else {
return
}
let x = (scrollView.contentOffset.x - scrollView.width) / scrollView.contentSize.width * scrollView.width
tb.line.center.x = selected.center.x + x
}
@objc
open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
selectedIndex = lround(Double(scrollView.contentOffset.x / scrollView.width)) selectedIndex = lround(Double(scrollView.contentOffset.x / scrollView.width))
} }
......
...@@ -87,7 +87,6 @@ public protocol TextViewDelegate : UITextViewDelegate { ...@@ -87,7 +87,6 @@ public protocol TextViewDelegate : UITextViewDelegate {
optional func textView(textView: TextView, didProcessEditing textStorage: TextStorage, text: String, range: NSRange) optional func textView(textView: TextView, didProcessEditing textStorage: TextStorage, text: String, range: NSRange)
} }
@objc(TextView)
open class TextView: UITextView { open class TextView: UITextView {
/// A boolean indicating whether the text is empty. /// A boolean indicating whether the text is empty.
open var isEmpty: Bool { open var isEmpty: Bool {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment