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 {
optional func pageTabBarController(pageTabBarController: PageTabBarController, didTransitionTo viewController: UIViewController)
}
@objc(PageTabBarController)
open class PageTabBarController: RootController {
/// Reference to the PageTabBar.
@IBInspectable
......
......@@ -44,6 +44,7 @@ open class TabMenuBarItem: FlatButton {
public enum TabMenuAlignment: Int {
case top
case bottom
case hidden
}
extension UIViewController {
......@@ -80,8 +81,37 @@ extension UIViewController {
open class TabMenuController: UIViewController {
@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] {
didSet {
oldValue.forEach {
......@@ -95,16 +125,12 @@ open class TabMenuController: UIViewController {
}
}
@IBInspectable
open var selectedIndex: Int {
open var tabMenuAlignment = TabMenuAlignment.bottom {
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.
- Parameter aDecoder: A NSCoder instance.
......@@ -143,6 +169,29 @@ open class TabMenuController: UIViewController {
open func layoutSubviews() {
layoutScrollView()
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 {
when subclassing.
*/
open func prepare() {
prepareTabBar()
prepareScrollView()
prepareViewControllers()
}
}
extension TabMenuController {
fileprivate func prepareTabBar() {
tabBar.isLineAnimated = false
tabBar.lineAlignment = .top
}
/// Prepares the scrollView used to pan through view controllers.
fileprivate func prepareScrollView() {
scrollView.delegate = self
scrollView.bounces = false
......@@ -174,15 +218,102 @@ extension TabMenuController {
view.addSubview(scrollView)
}
/// Prepares the view controllers.
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 {
addChildViewController(v)
scrollView.addSubview(v.view)
v.didMove(toParentViewController: self)
v.view.clipsToBounds = true
v.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
v.view.contentScaleFactor = Screen.scale
let button = v.tabMenuBarItem as UIButton
buttons.append(button)
}
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 {
fileprivate func layoutScrollView() {
scrollView.frame = view.bounds
scrollView.contentSize = CGSize(width: scrollView.width * CGFloat(viewControllers.count), height: scrollView.height)
print(selectedIndex)
scrollView.contentOffset = CGPoint(x: scrollView.width * CGFloat(selectedIndex), y: 0)
}
fileprivate func layoutViewControllers() {
for i in 0..<viewControllers.count {
let v = viewControllers[i]
v.view.frame = CGRect(x: CGFloat(i) * scrollView.width, y: 0, width: scrollView.width, height: scrollView.height)
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]
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 {
@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) {
selectedIndex = lround(Double(scrollView.contentOffset.x / scrollView.width))
}
......
......@@ -87,7 +87,6 @@ public protocol TextViewDelegate : UITextViewDelegate {
optional func textView(textView: TextView, didProcessEditing textStorage: TextStorage, text: String, range: NSRange)
}
@objc(TextView)
open class TextView: UITextView {
/// A boolean indicating whether the text is empty.
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