Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues resolved, more dynamic and useful #27

Merged
merged 1 commit into from
Jul 18, 2021
Merged
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
80 changes: 51 additions & 29 deletions CircleBar/Classes/SHCircleBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,43 @@
import UIKit

@IBDesignable class SHCircleBar: UITabBar {
var tabWidth: CGFloat = 0
var index: CGFloat = 0 {
private var tabWidth: CGFloat = 0
private var index: CGFloat = 0 {
willSet{
self.previousIndex = index
}
}
private var animated = false
private var selectedImage: UIImage?
private var previousIndex: CGFloat = 0

override init(frame: CGRect) {
super.init(frame: frame)
customInit()
}

required init?(coder aDecoder: NSCoder) {
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
customInit()

}
override func draw(_ rect: CGRect) {

open override func draw(_ rect: CGRect) {
drawCurve()
}
}

extension SHCircleBar {

func select(itemAt: Int, animated: Bool) {
self.index = CGFloat(itemAt)
self.animated = animated
self.selectedImage = self.selectedItem?.selectedImage
self.selectedItem?.selectedImage = nil
self.setNeedsDisplay()
}

private func drawCurve() {
let fillColor: UIColor = .white
tabWidth = self.bounds.width / CGFloat(self.items!.count)
let bezPath = drawPath(for: index)
Expand All @@ -50,40 +67,45 @@ import UIKit
mask.add(bezAnimation, forKey: nil)
}
self.layer.mask = mask

}

func select(itemAt: Int, animated: Bool) {
self.index = CGFloat(itemAt)
self.animated = animated
self.selectedImage = self.selectedItem?.selectedImage
self.selectedItem?.selectedImage = nil
self.setNeedsDisplay()
}

func customInit(){
private func customInit(){
self.tintColor = .white
self.barTintColor = .white
self.backgroundColor = .white
}

private func drawPath(for index: CGFloat) -> UIBezierPath {
let bezPath = UIBezierPath()

let firstPoint = CGPoint(x: (index * tabWidth) - 25, y: 0)
let firstPointFirstCurve = CGPoint(x: ((tabWidth * index) + tabWidth / 4), y: 0)
let firstPointSecondCurve = CGPoint(x: ((index * tabWidth) - 25) + tabWidth / 8, y: 52)

let middlePoint = CGPoint(x: (tabWidth * index) + tabWidth / 2, y: 55)
let middlePointFirstCurve = CGPoint(x: (((tabWidth * index) + tabWidth) - tabWidth / 8) + 25, y: 52)
let middlePointSecondCurve = CGPoint(x: (((tabWidth * index) + tabWidth) - tabWidth / 4), y: 0)

let lastPoint = CGPoint(x: (tabWidth * index) + tabWidth + 25, y: 0)
bezPath.move(to: firstPoint)
bezPath.addCurve(to: middlePoint, controlPoint1: firstPointFirstCurve, controlPoint2: firstPointSecondCurve)
bezPath.addCurve(to: lastPoint, controlPoint1: middlePointFirstCurve, controlPoint2: middlePointSecondCurve)

let tabHeight: CGFloat = tabWidth

let leftPoint = CGPoint(x: (index * tabWidth), y: 0)
let leftPointCurveUp = CGPoint(
x: ((tabWidth * index) + tabWidth / 5),
y: 0)
let leftPointCurveDown = CGPoint(
x: ((index * tabWidth) - tabWidth*0.2) + tabWidth / 4,
y: tabHeight*0.40)

let middlePoint = CGPoint(
x: (tabWidth * index) + tabWidth / 2,
y: tabHeight*0.4)
let middlePointCurveDown = CGPoint(
x: (((index * tabWidth) - tabWidth*0.2) + tabWidth / 10) + tabWidth,
y: tabHeight*0.40)
let middlePointCurveUp = CGPoint(
x: (((tabWidth * index) + tabWidth) - tabWidth / 5),
y: 0)

let rightPoint = CGPoint(x: (tabWidth * index) + tabWidth, y: 0)
bezPath.move(to: leftPoint)
bezPath.addCurve(to: middlePoint, controlPoint1: leftPointCurveUp, controlPoint2: leftPointCurveDown)
bezPath.addCurve(to: rightPoint, controlPoint1: middlePointCurveDown, controlPoint2: middlePointCurveUp)

bezPath.append(UIBezierPath(rect: self.bounds))

return bezPath
}


}

185 changes: 108 additions & 77 deletions CircleBar/Classes/SHCircleBarController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,82 +10,52 @@ import UIKit

class SHCircleBarController: UITabBarController {

fileprivate var shouldSelectOnTabBar = true
private var circleView : UIView!
private var circleImageView: UIImageView!
open override var selectedViewController: UIViewController? {

public override var selectedViewController: UIViewController? {
willSet {
guard shouldSelectOnTabBar, let newValue = newValue else {
shouldSelectOnTabBar = true
return
}
guard let tabBar = tabBar as? SHCircleBar, let index = viewControllers?.index(of: newValue) else {return}
guard let newValue = newValue,
let tabBar = tabBar as? SHCircleBar,
let index = viewControllers?.firstIndex(of: newValue)
else { return }
updateCircle(index: index)
tabBar.select(itemAt: index, animated: true)
}
}

open override var selectedIndex: Int {
public override var selectedIndex: Int {
willSet {
guard shouldSelectOnTabBar else {
shouldSelectOnTabBar = true
return
}
guard let tabBar = tabBar as? SHCircleBar else {
return
}
tabBar.select(itemAt: selectedIndex, animated: true)
guard let tabBar = tabBar as? SHCircleBar else { return }
updateCircle(index: newValue)
tabBar.select(itemAt: newValue, animated: true)
}
}

open override func viewDidLoad() {
super.viewDidLoad()
let tabBar = SHCircleBar()
self.setValue(tabBar, forKey: "tabBar")

self.circleView = UIView(frame: .zero)
circleView.layer.cornerRadius = 30
circleView.backgroundColor = .white
circleView.isUserInteractionEnabled = false

self.circleImageView = UIImageView(frame: .zero)
circleImageView.layer.cornerRadius = 30
circleImageView.isUserInteractionEnabled = false
circleImageView.contentMode = .center

circleView.addSubview(circleImageView)
self.view.addSubview(circleView)
let tabWidth = self.view.bounds.width / CGFloat(self.tabBar.items?.count ?? 4)

circleView.frame = CGRect(x: tabWidth / 2 - 30, y: self.tabBar.frame.origin.y - 40, width: 60, height: 60)
circleImageView.frame = self.circleView.bounds
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
circleImageView.image = image(with: self.tabBar.selectedItem?.image ?? self.tabBar.items?.first?.image, scaledTo: CGSize(width: 30, height: 30))

}

private var _barHeight: CGFloat = 74
open var barHeight: CGFloat {
public var barHeight: CGFloat {
get {
if #available(iOS 11.0, *) {
return _barHeight + view.safeAreaInsets.bottom
} else {
return _barHeight
}
} else { return _barHeight }
}
set {
_barHeight = newValue
updateTabBarFrame()
}
}

private func updateTabBarFrame() {
var tabFrame = self.tabBar.frame
tabFrame.size.height = barHeight
tabFrame.origin.y = self.view.frame.size.height - barHeight
self.tabBar.frame = tabFrame
tabBar.setNeedsLayout()
open override func viewDidLoad() {
super.viewDidLoad()
let tabBar = SHCircleBar()
self.setValue(tabBar, forKey: "tabBar")

addCirleView()
}

open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
circleImageView.image = self.tabBar.selectedItem?.image ?? self.tabBar.items?.first?.image
}

open override func viewWillLayoutSubviews() {
Expand All @@ -101,31 +71,92 @@ class SHCircleBarController: UITabBarController {
}

open override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
guard let idx = tabBar.items?.index(of: item) else { return }
if idx != selectedIndex, let controller = viewControllers?[idx] {
shouldSelectOnTabBar = false
selectedIndex = idx
let tabWidth = self.view.bounds.width / CGFloat(self.tabBar.items!.count)
UIView.animate(withDuration: 0.3) {
self.circleView.frame = CGRect(x: (tabWidth * CGFloat(idx) + tabWidth / 2 - 30), y: self.tabBar.frame.origin.y - 15, width: 60, height: 60)
}
UIView.animate(withDuration: 0.15, animations: {
self.circleImageView.alpha = 0
}) { (_) in
self.circleImageView.image = self.image(with: item.image, scaledTo: CGSize(width: 30, height: 30))
UIView.animate(withDuration: 0.15, animations: {
self.circleImageView.alpha = 1
})
}
delegate?.tabBarController?(self, didSelect: controller)
guard let index = tabBar.items?.firstIndex(of: item) else { return }
updateCircle(index: index)
}

}

extension SHCircleBarController {
public func updateCircle(index: Int) {
guard let items = tabBar.items,
let vcs = viewControllers,
index < items.count,
index < vcs.count,
index != selectedIndex else { return }

let item = items[index]
let controller = vcs[index]

let tabWidth = self.view.bounds.width / CGFloat(items.count)
let circleWidth = self.circleView.bounds.width

UIView.animate(withDuration: 0.3) { [weak self] in
guard let `self` = self else { return }
self.circleView.frame = CGRect(
x: (tabWidth * CGFloat(index) + tabWidth / 2 - circleWidth*0.5),
y: self.circleView.frame.minY,
width: circleWidth,
height: circleWidth)
}

UIView.animate(withDuration: 0.15) { [weak self] in
self?.circleImageView.alpha = 0
} completion: { [weak self] (_) in
self?.circleImageView.image = item.image
UIView.animate(withDuration: 0.15, animations: { [weak self] in
self?.circleImageView.alpha = 1
})
}
delegate?.tabBarController?(self, didSelect: controller)
}
private func image(with image: UIImage?, scaledTo newSize: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(newSize, _: false, _: 0.0)
image?.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage

fileprivate func updateTabBarFrame() {
var tabFrame = self.tabBar.frame
tabFrame.size.height = barHeight
tabFrame.origin.y = self.view.frame.size.height - barHeight
self.tabBar.frame = tabFrame
tabBar.setNeedsLayout()
}

fileprivate func addCirleView() {
let tabWidth = self.view.bounds.width / CGFloat(self.tabBar.items?.count ?? 4)
let circleViewWidth = tabWidth*0.5
let circleViewRadius = circleViewWidth*0.5

self.circleView = UIView(frame: .zero)
circleView.layer.cornerRadius = circleViewRadius
circleView.backgroundColor = .white

self.circleImageView = UIImageView(frame: .zero)
circleImageView.layer.cornerRadius = circleViewRadius
circleImageView.isUserInteractionEnabled = false
circleImageView.contentMode = .center

circleView.addSubview(circleImageView)
self.view.addSubview(circleView)

circleView.layer.shadowOffset = CGSize(width: 0, height: 0)
circleView.layer.shadowRadius = 2
circleView.layer.shadowColor = UIColor.black.cgColor
circleView.layer.shadowOpacity = 0.15

let bottomPadding = getBottomPadding()

circleView.frame = CGRect(
x: tabWidth / 2 - tabWidth*0.25,
y: self.tabBar.frame.origin.y - bottomPadding - circleViewWidth*0.5,
width: circleViewWidth,
height: circleViewWidth)
circleImageView.frame = self.circleView.bounds
}

fileprivate func getBottomPadding() -> CGFloat {
if #available(iOS 13.0, *) {
let window = UIApplication.shared.windows[0]
return window.safeAreaInsets.bottom
} else if #available(iOS 11.0, *) {
return view.safeAreaInsets.bottom
} else { return 0 }
}
}
4 changes: 0 additions & 4 deletions Example/CircleBar.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,11 @@
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-CircleBar_Example/Pods-CircleBar_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/CircleBar/CircleBar.framework",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
);
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CircleBar.framework",
);
Expand Down
6 changes: 3 additions & 3 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PODS:
- CircleBar (0.1.0)
- CircleBar (0.8.2)

DEPENDENCIES:
- CircleBar (from `../`)
Expand All @@ -9,8 +9,8 @@ EXTERNAL SOURCES:
:path: "../"

SPEC CHECKSUMS:
CircleBar: b9c26c3833a648ef0bd56102d9e15c2dc050588f
CircleBar: 5b882eb3870de5bbda42bef1b2ec6c5c533497db

PODFILE CHECKSUM: c85bb2d45d7d4fdfeeaa049dfcacccb3a811d08b

COCOAPODS: 1.6.0.beta.2
COCOAPODS: 1.10.1
Loading