diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 2016bc1..ae20398 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ 822B45C652E91993B8282A044A57EF87 /* SSCustomTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68C77BE5AB4A98CD6FC3CC8CE0BC0C12 /* SSCustomTabBar.swift */; }; 8B32BEBAA9986011CF99CF7A120FF2D3 /* Pods-SSCustomTabbar_Example-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 439C96106A69ECD29AB89894B1B43113 /* Pods-SSCustomTabbar_Example-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8B6195B43F14646580DD007FEB39B9C2 /* Pods-SSCustomTabbar_Tests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 99331EA7304B9C656E1EFCD6D7907A69 /* Pods-SSCustomTabbar_Tests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B34C10EA292E664F00E12A79 /* SwiftUITabbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C10E9292E664F00E12A79 /* SwiftUITabbar.swift */; }; + B34C10EC292E667700E12A79 /* SwiftUISupport.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B34C10EB292E667700E12A79 /* SwiftUISupport.storyboard */; }; CDCEF463D2DD0BCFE667523E6404238A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 436BAA54A31999B53B3CC7115C55FE50 /* Foundation.framework */; }; E8D14FDBFD8AEB7524611CF690C69F62 /* SSCustomTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F9F9296D33BB3E4F4779BD24D352620 /* SSCustomTabBarViewController.swift */; }; F586E30617880208004373363F297E72 /* Pods-SSCustomTabbar_Tests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BBD9402A44AFFA17DEC2AB27EAE84FD3 /* Pods-SSCustomTabbar_Tests-dummy.m */; }; @@ -68,6 +70,8 @@ AF5D39AF5CC89874601B31847FF5FE38 /* Pods-SSCustomTabbar_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SSCustomTabbar_Tests.debug.xcconfig"; sourceTree = ""; }; AF780B59199CB7438C6D25D9004FE5BB /* SSCustomTabbar.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SSCustomTabbar.modulemap; sourceTree = ""; }; B232FF61468F97F937CF720DF50DB934 /* Pods-SSCustomTabbar_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SSCustomTabbar_Example.release.xcconfig"; sourceTree = ""; }; + B34C10E9292E664F00E12A79 /* SwiftUITabbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftUITabbar.swift; path = SSCustomTabBar/Classes/SwiftUITabbar.swift; sourceTree = ""; }; + B34C10EB292E667700E12A79 /* SwiftUISupport.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SwiftUISupport.storyboard; path = SSCustomTabBar/SwiftUI/SwiftUISupport.storyboard; sourceTree = ""; }; B93385B9E28534B3DB5E36B54221B1DB /* Pods-SSCustomTabbar_Example-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-SSCustomTabbar_Example-acknowledgements.plist"; sourceTree = ""; }; BBD9402A44AFFA17DEC2AB27EAE84FD3 /* Pods-SSCustomTabbar_Tests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-SSCustomTabbar_Tests-dummy.m"; sourceTree = ""; }; CCD3D61FFB9FA21D87EE945CDCB89030 /* SSCustomTabbar.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SSCustomTabbar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -115,6 +119,8 @@ 68C77BE5AB4A98CD6FC3CC8CE0BC0C12 /* SSCustomTabBar.swift */, 6F9F9296D33BB3E4F4779BD24D352620 /* SSCustomTabBarViewController.swift */, F2FB3B1595472826972D32585D2AB6BE /* UIView+Extenstion.swift */, + B34C10E9292E664F00E12A79 /* SwiftUITabbar.swift */, + B34C10EB292E667700E12A79 /* SwiftUISupport.storyboard */, D4C7B9D0D58BA5195EFE740D1F3ED117 /* Pod */, 509B4A2E661444D647F0D0E1254B39AD /* Support Files */, ); @@ -359,6 +365,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + B34C10EC292E667700E12A79 /* SwiftUISupport.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -400,6 +407,7 @@ buildActionMask = 2147483647; files = ( FB16F272CD01B1C2C89162410E741C64 /* SSCustomTabbar-dummy.m in Sources */, + B34C10EA292E664F00E12A79 /* SwiftUITabbar.swift in Sources */, 822B45C652E91993B8282A044A57EF87 /* SSCustomTabBar.swift in Sources */, E8D14FDBFD8AEB7524611CF690C69F62 /* SSCustomTabBarViewController.swift in Sources */, 63D357BC50EFE66ACD623EADD58138F2 /* UIView+Extenstion.swift in Sources */, diff --git a/Example/SSCustomTabbar/Main.storyboard b/Example/SSCustomTabbar/Main.storyboard index 1ff8405..eb6ec6b 100644 --- a/Example/SSCustomTabbar/Main.storyboard +++ b/Example/SSCustomTabbar/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -16,7 +16,7 @@ - + - @@ -44,8 +44,8 @@ - + @@ -72,8 +72,9 @@ - + + @@ -100,8 +101,8 @@ - + @@ -116,8 +117,8 @@ - + @@ -132,8 +133,8 @@ - + diff --git a/README.md b/README.md index e493429..93c3821 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ ![Example](https://raw.githubusercontent.com/simformsolutions/SSCustomTabbar/master/SSCustomTabBar/Screenshots/customTabbar.gif) +![Example](https://raw.githubusercontent.com/simformsolutions/SSCustomTabbar/master/SSCustomTabBar/Screenshots/reverseTabbar.gif) + ## Requirements - iOS 11.0+ @@ -33,6 +35,10 @@ pod 'SSCustomTabbar' ### Set UITabBar to SSCustomTabBar ![alt text](https://raw.githubusercontent.com/simformsolutions/SSCustomTabbar/master/SSCustomTabBar/Screenshots/SSCustomTabBar.png) +### Set UITabBar to SSCustomTabBar reverse curve +![alt text](https://raw.githubusercontent.com/simformsolutions/SSCustomTabbar/master/SSCustomTabBar/Screenshots/SSCustomTabBarReverseCurve.png) + + ## SwiftUI Usage example struct TabItem: View { @@ -104,6 +110,28 @@ You can change: ![alt text](https://raw.githubusercontent.com/simformsolutions/SSCustomTabbar/master/SSCustomTabBar/Screenshots/Description.png) + +## Adding Badge value +![alt text](https://raw.githubusercontent.com/simformsolutions/SSCustomTabbar/master/SSCustomTabBar/Screenshots/SSCustomTabBarBadgeValue.png) + +Add/Update badge value: +``` +let tabBarController = self.tabBarController as? SSCustomTabBarViewController +tabBarController?.addOrUpdateBadgeValueAtIndex(index: 0, value: "Your Value Here") +``` + +Remove badge value: +``` +let tabBarController = self.tabBarController as? SSCustomTabBarViewController +tabBarController?.removeBadgeValueAtIndex(index: 1) +``` + +Remove all badge values: +``` +let tabBarController = self.tabBarController as? SSCustomTabBarViewController +tabBarController?.removeAllBadges() +``` + ## Contribute We would love you for the contribution to SSCustomTabMenu, check the LICENSE file for more info. diff --git a/SSCustomTabBar/Classes/SSCustomTabBar.swift b/SSCustomTabBar/Classes/SSCustomTabBar.swift index 2978167..9fc16fb 100644 --- a/SSCustomTabBar/Classes/SSCustomTabBar.swift +++ b/SSCustomTabBar/Classes/SSCustomTabBar.swift @@ -10,35 +10,29 @@ import UIKit public class SSCustomTabBar: UITabBar { - /// Fill color of back wave layer @IBInspectable var layerFillColor: UIColor { get { return UIColor(cgColor: kLayerFillColor) - } - set{ + } set { kLayerFillColor = newValue.cgColor } } - /// Wave Height @IBInspectable var waveHeight: CGFloat { - get{ + get { return self.minimalHeight - } - set{ + } set { self.minimalHeight = newValue } } - /// Unselected item tint color @IBInspectable var unselectedTabTintColor: UIColor { get { return self.unselectedItemTintColor ?? .black - } - set{ + } set { self.unselectedItemTintColor = newValue } } @@ -47,8 +41,7 @@ public class SSCustomTabBar: UITabBar { @IBInspectable var shadowColor: UIColor { get { return UIColor(cgColor: self.layer.shadowColor ?? UIColor.clear.cgColor) - } - set{ + } set { self.layer.shadowColor = newValue.cgColor } } @@ -57,8 +50,7 @@ public class SSCustomTabBar: UITabBar { @IBInspectable var shadowRadius: CGFloat { get { return layer.shadowRadius - } - set{ + } set { self.layer.shadowRadius = newValue } } @@ -72,10 +64,20 @@ public class SSCustomTabBar: UITabBar { } } + /// Reverse Curve + @IBInspectable var reverseCurve: Bool { + get { + return reverseCurveShape + } set { + self.reverseCurveShape = newValue + } + } + private var kLayerFillColor: CGColor = UIColor.blue.cgColor private var displayLink: CADisplayLink! private let tabBarShapeLayer = CAShapeLayer() internal var minimalHeight: CGFloat = 30 + internal var reverseCurveShape: Bool = false private var minimalY: CGFloat { get { return -minimalHeight @@ -89,7 +91,6 @@ public class SSCustomTabBar: UITabBar { } /// Controll point of wave - private var leftPoint4 = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 3.0, height: 3.0)) { didSet { leftPoint4.backgroundColor = .clear @@ -136,9 +137,7 @@ public class SSCustomTabBar: UITabBar { } } - /// Draws the receiver’s image within the passed-in rectangle. - /// /// - Parameter rect: rect of view override public func draw(_ rect: CGRect) { super.draw(rect) @@ -152,7 +151,6 @@ public class SSCustomTabBar: UITabBar { } - // MARK: - Setup Tabbar extension SSCustomTabBar { @@ -162,13 +160,11 @@ extension SSCustomTabBar { self.backgroundImage = UIImage() self.shadowImage = UIImage() self.clipsToBounds = false - /// Shadow self.layer.shadowOffset = shadowOffset self.layer.shadowRadius = shadowRadius self.layer.shadowColor = shadowColor.cgColor self.layer.shadowOpacity = 1.0 - self.addSubview(leftPoint4) self.addSubview(leftPoint3) self.addSubview(leftPoint2) @@ -181,61 +177,67 @@ extension SSCustomTabBar { self.displayLink = CADisplayLink(target: self, selector: #selector(updateShapeLayer)) self.displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.default) self.displayLink?.isPaused = true - tabBarShapeLayer.frame = CGRect(x: 0.0, y: 0, width: self.bounds.width, height: self.bounds.height) tabBarShapeLayer.actions = ["position" : NSNull(), "bounds" : NSNull(), "path" : NSNull()] tabBarShapeLayer.fillColor = kLayerFillColor self.layer.insertSublayer(tabBarShapeLayer, at: 0) - let width = UIScreen.main.bounds.width/CGFloat(self.items?.count ?? 0) if let selectedItem = self.selectedItem { let index = (self.items?.firstIndex(of: selectedItem) ?? 0)+1 let changeValue = (width*(CGFloat(index)))-(width/2) - self.setDefaultlayoutControlPoints(waveHeight: minimalHeight, locationX: changeValue) + if(reverseCurveShape) { + self.setReverselayoutControlPoints(waveHeight: minimalHeight, locationX: changeValue) + } else { + self.setDefaultlayoutControlPoints(waveHeight: minimalHeight, locationX: changeValue) + } self.updateShapeLayer() } } } - - // MARK: - Set layer path extension SSCustomTabBar { func setDefaultlayoutControlPoints(waveHeight: CGFloat, locationX: CGFloat) { - let width = (UIScreen.main.bounds.width/CGFloat(self.items?.count ?? 0)) leftPoint4.center = CGPoint(x: 0, y: minimalY+minimalHeight) rightPoint4.center = CGPoint(x: self.bounds.width, y: minimalY+minimalHeight) - - let imaganaeryFram = CGRect(x: locationX-(width/2), y: minimalY, width: width, height: minimalHeight) - - leftPoint3.center = CGPoint(x: imaganaeryFram.minX, y: imaganaeryFram.maxY) - - - let topOffset: CGFloat = imaganaeryFram.width / 4.3 - let bottomOffset: CGFloat = imaganaeryFram.width / 4.5 - - leftPoint2.center = CGPoint(x: imaganaeryFram.midX, y: imaganaeryFram.minY) - leftPoint1.center = CGPoint(x: imaganaeryFram.minX + bottomOffset, y: imaganaeryFram.maxY) - centerPoint1.center = CGPoint(x: imaganaeryFram.midX - topOffset, y: imaganaeryFram.minY) - centerPoint2.center = CGPoint(x: imaganaeryFram.maxX, y: imaganaeryFram.maxY) - rightPoint1.center = CGPoint(x: imaganaeryFram.midX + topOffset, y: imaganaeryFram.minY) - rightPoint2.center = CGPoint(x: imaganaeryFram.maxX - bottomOffset, y: imaganaeryFram.maxY) + let imaginaryFrame = CGRect(x: locationX-(width/2), y: minimalY, width: width, height: minimalHeight) + leftPoint3.center = CGPoint(x: imaginaryFrame.minX, y: imaginaryFrame.maxY) + let topOffset: CGFloat = imaginaryFrame.width / 4.3 + let bottomOffset: CGFloat = imaginaryFrame.width / 4.5 + leftPoint2.center = CGPoint(x: imaginaryFrame.midX, y: imaginaryFrame.minY) + leftPoint1.center = CGPoint(x: imaginaryFrame.minX + bottomOffset, y: imaginaryFrame.maxY) + centerPoint1.center = CGPoint(x: imaginaryFrame.midX - topOffset, y: imaginaryFrame.minY) + centerPoint2.center = CGPoint(x: imaginaryFrame.maxX, y: imaginaryFrame.maxY) + rightPoint1.center = CGPoint(x: imaginaryFrame.midX + topOffset, y: imaginaryFrame.minY) + rightPoint2.center = CGPoint(x: imaginaryFrame.maxX - bottomOffset, y: imaginaryFrame.maxY) } + func setReverselayoutControlPoints(waveHeight: CGFloat, locationX: CGFloat) { + let width = (UIScreen.main.bounds.width/CGFloat(self.items?.count ?? 0)) + leftPoint4.center = CGPoint(x: 0, y: minimalY+minimalHeight) + rightPoint4.center = CGPoint(x: self.bounds.width, y: -(minimalY+minimalHeight)) + let imaginaryFrame = CGRect(x: locationX-(width/2), y: minimalY, width: width, height: minimalHeight) + leftPoint3.center = CGPoint(x: imaginaryFrame.minX, y: -imaginaryFrame.maxY) + let topOffset: CGFloat = imaginaryFrame.width / 4.3 + let bottomOffset: CGFloat = imaginaryFrame.width / 4.5 + leftPoint2.center = CGPoint(x: imaginaryFrame.midX, y: -imaginaryFrame.minY) + leftPoint1.center = CGPoint(x: imaginaryFrame.minX + bottomOffset, y: -imaginaryFrame.maxY) + centerPoint1.center = CGPoint(x: imaginaryFrame.midX - topOffset, y: -imaginaryFrame.minY) + centerPoint2.center = CGPoint(x: imaginaryFrame.maxX, y: -imaginaryFrame.maxY) + rightPoint1.center = CGPoint(x: imaginaryFrame.midX + topOffset, y: -imaginaryFrame.minY) + rightPoint2.center = CGPoint(x: imaginaryFrame.maxX - bottomOffset, y: -imaginaryFrame.maxY) + } /// updateShapeLayer @objc func updateShapeLayer() { tabBarShapeLayer.path = getCurrentPath() } - /// Get path - /// /// - Returns: get current index path func getCurrentPath() -> CGPath { - let bezierPath = UIBezierPath() bezierPath.move(to: CGPoint(x: 0.0, y: UIScreen.main.bounds.height)) bezierPath.addLine(to: CGPoint(x: 0.0, y: leftPoint4.viewCenter(usePresentationLayerIfPossible: animating).y)) @@ -245,18 +247,16 @@ extension SSCustomTabBar { controlPoint1: leftPoint1.viewCenter(usePresentationLayerIfPossible: animating), controlPoint2: centerPoint1.viewCenter(usePresentationLayerIfPossible: animating) ) - bezierPath.addCurve( to: centerPoint2.viewCenter(usePresentationLayerIfPossible: animating), controlPoint1: rightPoint1.viewCenter(usePresentationLayerIfPossible: animating), controlPoint2: rightPoint2.viewCenter(usePresentationLayerIfPossible: animating) ) - bezierPath.addLine(to: leftPoint3.viewCenter(usePresentationLayerIfPossible: animating)) - bezierPath.addLine(to: rightPoint4.viewCenter(usePresentationLayerIfPossible: animating)) bezierPath.addLine(to: CGPoint(x: UIScreen.main.bounds.width, y: UIScreen.main.bounds.height)) bezierPath.close() return bezierPath.cgPath } + } diff --git a/SSCustomTabBar/Classes/SSCustomTabBarViewController.swift b/SSCustomTabBar/Classes/SSCustomTabBarViewController.swift index fafdc0d..232fdf2 100644 --- a/SSCustomTabBar/Classes/SSCustomTabBarViewController.swift +++ b/SSCustomTabBar/Classes/SSCustomTabBarViewController.swift @@ -9,16 +9,13 @@ import UIKit -/// Default index value for priviousSelectedIndex -private let defaultIndexValue = -1 - public class SSCustomTabBarViewController: UITabBarController { // MARK: - Overrides - public override var selectedIndex: Int { didSet { guard let items = self.tabBar.items else { return } + currentIndex = selectedIndex if items.indices.contains(selectedIndex) { let item = items[selectedIndex] self.tabBar(tabBar, didSelect: item) @@ -34,29 +31,36 @@ public class SSCustomTabBarViewController: UITabBarController { /// Tabbar height @IBInspectable var barHeight: CGFloat { - get{ + get { return self.kBarHeight ?? self.tabBar.frame.height - } - set{ + } set { self.kBarHeight = newValue } } /// icon up animation point @IBInspectable var upAnimationPoint: CGFloat { - get{ + get { return self.kUpAnimationPoint - } - set{ + } set { self.kUpAnimationPoint = newValue } } private var kBarHeight: CGFloat? - private var kUpAnimationPoint: CGFloat = 20 + private var previousSelectedIndex: Int = 0 + + var orderedTabBarItemViews: [UIView] { + get { + return tabBar.subviews.filter({ $0 is UIControl }) + .sorted(by: { $0.frame.minX < $1.frame.minX }) + } + } - private var priviousSelectedIndex: Int = defaultIndexValue + private var currentIndex = 0 + private let animationDuration = 0.9 + private let animationSpring: CGFloat = 0.57 override public func viewDidLoad() { super.viewDidLoad() @@ -64,9 +68,7 @@ public class SSCustomTabBarViewController: UITabBarController { // Do any additional setup after loading the view. } - /// Notifies the view controller that its view was added to a view hierarchy. - /// /// - Parameter animated: variable for namiation override public func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) @@ -75,7 +77,7 @@ public class SSCustomTabBarViewController: UITabBarController { func setup() { guard let count = tabBar.items?.count, count > 0 else { return } - if self.priviousSelectedIndex == defaultIndexValue { + if self.previousSelectedIndex == 0 { if let item = self.tabBar.selectedItem { self.tabBar(self.tabBar, didSelect: item) } @@ -83,26 +85,22 @@ public class SSCustomTabBarViewController: UITabBarController { self.applicationDidBecomeActive() } - /// setObserver func setObserver() { - NotificationCenter.default.addObserver(self, - selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) + NotificationCenter.default.addObserver(self,selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: Notification.Name(rawValue: SSConstants.updateViewNotification), object: nil) } - /// removeObserver func removeObserver() { NotificationCenter.default.removeObserver(self) } - /// call when application become active @objc func applicationDidBecomeActive() { DispatchQueue.main.async { [weak self] in guard let uSelf = self else { return } - let view = uSelf.getUpView(index: uSelf.selectedIndex) + let view = uSelf.getUpView(index: uSelf.currentIndex) if view.frame.origin.y > 0 { view.frame.origin.y -= uSelf.kUpAnimationPoint } @@ -112,11 +110,13 @@ public class SSCustomTabBarViewController: UITabBarController { deinit { self.removeObserver() } + } // MARK: - set bar height extension SSCustomTabBarViewController { + override public func viewWillLayoutSubviews() { guard var height = kBarHeight else { return } height += self.view.safeAreaInsets.bottom @@ -126,79 +126,135 @@ extension SSCustomTabBarViewController { self.tabBar.frame = tabBarFrame self.tabBar.clipsToBounds = false } + } // MARK: - Tabbar Delegate extension SSCustomTabBarViewController { - - /// Sent to the delegate when the user selects a tab bar item. - /// /// - Parameters: /// - tabBar: The tab bar that is being customized. /// - item: The tab bar item that was selected. override public func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) { + guard let uSelf = self.tabBar as? SSCustomTabBar, + let items = uSelf.items, + let index = items.firstIndex(of: item), + index != self.previousSelectedIndex else { return } - if let uSelf = self.tabBar as? SSCustomTabBar, let items = uSelf.items, let index = items.firstIndex(of: item), index != self.priviousSelectedIndex { - - let width = UIScreen.main.bounds.width/CGFloat(items.count) - let changeValue = (width*CGFloat(index+1))-(width/2) - uSelf.animating = true - - let orderedTabBarItemViews: [UIView] = { - let interactionViews = tabBar.subviews.filter({ $0 is UIControl }) - return interactionViews.sorted(by: { $0.frame.minX < $1.frame.minX }) - }() - - orderedTabBarItemViews.forEach({ (objectView) in - let objectIndex = orderedTabBarItemViews.firstIndex(of: objectView) - if index == objectIndex { - print(index) - }else if objectIndex == priviousSelectedIndex { - UIView.animate(withDuration: 0.9, delay: 0.0, usingSpringWithDamping: 0.57, initialSpringVelocity: 0.0, options: .curveEaseInOut, animations: { - objectView.frame = CGRect(x: objectView.frame.origin.x, y: objectView.frame.origin.y + self.kUpAnimationPoint, width: objectView.frame.width, height: objectView.frame.height) - }, completion: nil) - } - }) - self.priviousSelectedIndex = index - performSpringAnimation(for: orderedTabBarItemViews[index], changeValue: changeValue) - } + let width = UIScreen.main.bounds.width/CGFloat(items.count) + let changeValue = (width*CGFloat(index+1))-(width/2) + uSelf.animating = true + + orderedTabBarItemViews.forEach({ (objectView) in + let objectIndex = orderedTabBarItemViews.firstIndex(of: objectView) + if index == objectIndex { + print(index) + } else if objectIndex == previousSelectedIndex { + UIView.animate(withDuration: animationDuration, delay: 0.0, usingSpringWithDamping: animationSpring, initialSpringVelocity: 0.0, options: .curveEaseInOut, animations: { + objectView.frame = CGRect(x: objectView.frame.origin.x, y: objectView.frame.origin.y + self.kUpAnimationPoint, width: objectView.frame.width, height: objectView.frame.height) + }, completion: nil) + } + }) + self.previousSelectedIndex = index + performSpringAnimation(for: orderedTabBarItemViews[index], changeValue: changeValue) + for (viewIndex, subViews) in self.tabBar.subviews.enumerated() { + for badgeView in subViews.subviews { + repositionBadgeView(badgeView, viewIndex == index ? -kUpAnimationPoint : 0) + } + } } - /// Get specific view from - /// /// - Parameter index: view index /// - Returns: specific view func getUpView(index: Int) -> UIView { - let orderedTabBarItemViews: [UIView] = { - let interactionViews = tabBar.subviews.filter({ $0 is UIControl }) - return interactionViews.sorted(by: { $0.frame.minX < $1.frame.minX }) - }() return orderedTabBarItemViews[index] } - /// Perform Animation - /// /// - Parameters: /// - view: going to up. /// - changeValue: center location for wave. func performSpringAnimation(for view: UIView, changeValue: CGFloat) { - - if let uSelf = self.tabBar as? SSCustomTabBar { - UIView.animate(withDuration: 0.9, delay: 0.0, usingSpringWithDamping: 0.57, initialSpringVelocity: 0.0, options: [], animations: { () -> Void in + guard let uSelf = self.tabBar as? SSCustomTabBar else { return } + UIView.animate(withDuration: animationDuration, delay: 0.0, usingSpringWithDamping: animationSpring, initialSpringVelocity: 0.0, options: [], animations: { () -> Void in + if(uSelf.reverseCurveShape) { + uSelf.setReverselayoutControlPoints(waveHeight: uSelf.minimalHeight, locationX: changeValue) + } else { uSelf.setDefaultlayoutControlPoints(waveHeight: uSelf.minimalHeight, locationX: changeValue) - - }, completion: { _ in - uSelf.animating = false - }) - UIView.animate(withDuration: 0.9, delay: 0.0, usingSpringWithDamping: 0.57, initialSpringVelocity: 0.0, options: .curveEaseInOut, animations: { - view.frame = CGRect(x: view.frame.origin.x, y: view.frame.origin.y - self.kUpAnimationPoint, width: view.frame.width, height: view.frame.height) - }, completion: nil) + } + + }, completion: { _ in + uSelf.animating = false + }) + + UIView.animate(withDuration: animationDuration, delay: 0.0, usingSpringWithDamping: animationSpring, initialSpringVelocity: 0.0, options: .curveEaseInOut, animations: { + view.frame = CGRect(x: view.frame.origin.x, y: view.frame.origin.y - self.kUpAnimationPoint, width: view.frame.width, height: view.frame.height) + }, completion: nil) + } + +} + +// MARK: - Badge related functions +extension SSCustomTabBarViewController { + + /// Reposition the badge view for tabbar item + /// + /// - Parameters: + /// - badgeView: The view of the badge + /// - yPosition: The change value in y axis + func repositionBadgeView(_ badgeView: UIView, _ yPosition: CGFloat) { + if NSStringFromClass(badgeView.classForCoder) == "_UIBadgeView" { + badgeView.layer.transform = CATransform3DIdentity + badgeView.layer.transform = CATransform3DMakeTranslation(1.0, yPosition, 1.0) + } + } + + /// Add a badge value at provided index + /// + /// - Parameters: + /// - index: index of the tabbar item to set badge + /// - value: value to set as a badge + open func addOrUpdateBadgeValueAtIndex(index: Int, value: String) { + guard let item = getTabBarItemAtIndex(index) else { return } + item.badgeValue = value + } + + + /// Remove a badge value at provided index + /// + /// - Parameters: + /// - index: index of the tabbar item to remove badge + open func removeBadgeValueAtIndex(index: Int) { + guard let item = getTabBarItemAtIndex(index) else { return } + item.badgeValue = nil + } + + + /// Remove all badge values + open func removeAllBadges() { + guard let numberOfItems = self.tabBar.items?.count else { return } + for index in 0...numberOfItems - 1 { + getTabBarItemAtIndex(index)?.badgeValue = nil + } + } + + + /// Provide a tabbar item at required index if present + /// + /// - Parameters: + /// - index: index of the tabbar item to get + /// - Return:TabBar item at the required index if present, otherwise returns nil + private func getTabBarItemAtIndex(_ index: Int) -> UITabBarItem? { + guard let items = self.tabBar.items else { + return nil + } + guard items.indices.contains(index) else { + return nil } + return items[index] } } diff --git a/SSCustomTabBar/Classes/SwiftUITabbar.swift b/SSCustomTabBar/Classes/SwiftUITabbar.swift index 387c3de..bbe2aa4 100644 --- a/SSCustomTabBar/Classes/SwiftUITabbar.swift +++ b/SSCustomTabBar/Classes/SwiftUITabbar.swift @@ -39,6 +39,12 @@ public class SSTabConfiguration { /// Shadow Offset public var shadowOffset: CGSize + /// Reverse Curve + public var reverseCurve: Bool + + /// Selected index + public var selectedIndex: Int + /// Initializer for Tabbar configuration /// - Parameters: /// - barHeight: Bar height @@ -50,7 +56,21 @@ public class SSTabConfiguration { /// - shadowColor: Shadow Color /// - shadowRadius: Shadow Radius /// - shadowOffset: Shadow Offset - public init(barHeight: CGFloat? = nil, upAnimationPoint: CGFloat? = nil, layerFillColor: UIColor = .white, waveHeight: CGFloat = 17, selectedTabTintColor: UIColor = UIColor.orange, unselectedTabTintColor: UIColor = .black, shadowColor: UIColor = .black, shadowRadius: CGFloat = .zero, shadowOffset: CGSize = CGSize(width: 0, height: -1)) { + /// - reverseCurve: Set reverse curve + /// - selectedIndex: Index of selected tabbar item + public init( + barHeight: CGFloat? = nil, + upAnimationPoint: CGFloat? = nil, + layerFillColor: UIColor = .white, + waveHeight: CGFloat = 17, + selectedTabTintColor: UIColor = UIColor.orange, + unselectedTabTintColor: UIColor = .black, + shadowColor: UIColor = .black, + shadowRadius: CGFloat = .zero, + shadowOffset: CGSize = CGSize(width: 0, height: -1), + reverseCurve: Bool = false, + selectedIndex: Int = 0 + ) { self.barHeight = barHeight self.upAnimationPoint = upAnimationPoint self.layerFillColor = layerFillColor @@ -60,6 +80,8 @@ public class SSTabConfiguration { self.shadowColor = shadowColor self.shadowRadius = shadowRadius self.shadowOffset = shadowOffset + self.reverseCurve = reverseCurve + self.selectedIndex = selectedIndex } } @@ -119,7 +141,6 @@ public struct SwiftUITabBarController: UIViewControllerRepresentable { private class SSBundle {} // MARK: - Initializers - /// SwiftUI Tabbar view controller /// - Parameters: /// - tabItems: Array of TabItems of type SwiftUITabView @@ -165,6 +186,11 @@ public struct SwiftUITabBarController: UIViewControllerRepresentable { tabBar.shadowOffset = configuration.shadowOffset tabBar.tintColor = configuration.selectedTabTintColor tabBar.isHidden = isTabBarHidden + tabBar.reverseCurve = configuration.reverseCurve + if let viewControllers = tabBarVC.viewControllers, + configuration.selectedIndex <= viewControllers.count { + tabBarVC.selectedIndex = configuration.selectedIndex + } } public static func refreshViews() { diff --git a/SSCustomTabBar/Screenshots/SSCustomTabBarBadgeValue.png b/SSCustomTabBar/Screenshots/SSCustomTabBarBadgeValue.png new file mode 100644 index 0000000..990d209 Binary files /dev/null and b/SSCustomTabBar/Screenshots/SSCustomTabBarBadgeValue.png differ diff --git a/SSCustomTabBar/Screenshots/SSCustomTabBarReverseCurve.png b/SSCustomTabBar/Screenshots/SSCustomTabBarReverseCurve.png new file mode 100644 index 0000000..6c1929a Binary files /dev/null and b/SSCustomTabBar/Screenshots/SSCustomTabBarReverseCurve.png differ diff --git a/SSCustomTabBar/Screenshots/reverseTabbar.gif b/SSCustomTabBar/Screenshots/reverseTabbar.gif new file mode 100644 index 0000000..71bd4eb Binary files /dev/null and b/SSCustomTabBar/Screenshots/reverseTabbar.gif differ diff --git a/SSCustomTabBar/SwiftUI/SwiftUISupport.storyboard b/SSCustomTabBar/SwiftUI/SwiftUISupport.storyboard index f6bd71d..f766989 100644 --- a/SSCustomTabBar/SwiftUI/SwiftUISupport.storyboard +++ b/SSCustomTabBar/SwiftUI/SwiftUISupport.storyboard @@ -1,9 +1,8 @@ - + - - + diff --git a/SSCustomTabbar.podspec b/SSCustomTabbar.podspec index dd7a80f..d492903 100644 --- a/SSCustomTabbar.podspec +++ b/SSCustomTabbar.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SSCustomTabbar' - s.version = '2.0.6' + s.version = '2.0.7' s.platform = :ios s.swift_version = '5.0' s.summary = 'Simple Animated tabbar with native control.' diff --git a/SwiftUI Example/Podfile.lock b/SwiftUI Example/Podfile.lock index 5149d40..858dcb5 100644 --- a/SwiftUI Example/Podfile.lock +++ b/SwiftUI Example/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - SSCustomTabbar (2.0.6) + - SSCustomTabbar (2.0.7) DEPENDENCIES: - SSCustomTabbar (from `../`) @@ -9,8 +9,8 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - SSCustomTabbar: a52533e429bd40f52ce8d7ecb801f3521fbd56a0 + SSCustomTabbar: 643270b6b771125ec173f58812cfab3da0296fd8 PODFILE CHECKSUM: c2ba52075d251c51d90601a7a42b231baf400bcc -COCOAPODS: 1.8.4 +COCOAPODS: 1.11.3 diff --git a/SwiftUI Example/SwiftUIExample/ContentView.swift b/SwiftUI Example/SwiftUIExample/ContentView.swift index 75ee870..b399c77 100644 --- a/SwiftUI Example/SwiftUIExample/ContentView.swift +++ b/SwiftUI Example/SwiftUIExample/ContentView.swift @@ -34,9 +34,7 @@ struct TabItem: View { NavigationLink(destination: PushedView(isTabBarHidden: self.$isTabBarHidden, showPushedView: self.$isNextActive), isActive: self.$isNextActive) { EmptyView() } - VStack(spacing: 30) { - Button(action: { self.isNextActive = true }) { @@ -69,8 +67,7 @@ struct ContentView: View { let vc3 = SwiftUITabView(content: UIHostingController(rootView: TabItem(text: "Video", isTabBarHidden: self.$isTabBarHidden)), title: "Video", selectedImage: "iconVideoSelected", unSelectedImage: "iconVideo") let vc4 = SwiftUITabView(content: UIHostingController(rootView: TabItem(text: "Profile", isTabBarHidden: self.$isTabBarHidden)), title: "Profile", selectedImage: "iconProfileSelected", unSelectedImage: "iconProfile") let vc5 = SwiftUITabView(content: UIHostingController(rootView: TabItem(text: "Chat", isTabBarHidden: self.$isTabBarHidden)), title: "Chat", selectedImage: "iconChatSelected", unSelectedImage: "iconChat") - - let tabBarView = SwiftUITabBarController(tabItems: [vc1, vc2, vc3, vc4, vc5], configuration: .constant(SSTabConfiguration()), isTabBarHidden: self.$isTabBarHidden) + let tabBarView = SwiftUITabBarController(tabItems: [vc1, vc2, vc3, vc4, vc5], configuration: .constant(SSTabConfiguration(reverseCurve:true)), isTabBarHidden: self.$isTabBarHidden) return tabBarView }