Dibya Malla
2 years ago
11 changed files with 530 additions and 4 deletions
-
12GME Remit.xcodeproj/project.pbxproj
-
11GME Remit/Extensions/String+Ext.swift
-
67GME Remit/Modules/Home/User Interface/View/Cell/CarouselCell.swift
-
207GME Remit/Modules/Home/User Interface/View/Custom/HomeCarouselView.swift
-
51GME Remit/Modules/Home/User Interface/View/Custom/HomeExchangeRateView.swift
-
2GME Remit/Modules/Home/User Interface/View/Home.storyboard
-
14GME Remit/Modules/Home/User Interface/View/HomeViewController.swift
-
21GME Remit/Supported Files/Assets.xcassets/exchangeRateButton.imageset/Contents.json
-
10GME Remit/Supported Files/Assets.xcassets/exchangeRateButton.imageset/exchangeRateButton.svg
-
130GME Remit/Utilities/CustomUI/CustomPageControl.swift
-
9GME Remit/Utilities/ExchangeRateCustomView.swift
@ -0,0 +1,67 @@ |
|||
// |
|||
|
|||
import Foundation |
|||
import UIKit |
|||
import SDWebImage |
|||
|
|||
|
|||
class CarouselCollectionCell: UICollectionViewCell{ |
|||
|
|||
static let identifier: String = "CarouselCollectionCellID" |
|||
|
|||
private let backgroundImgView: UIImageView = { |
|||
let view = UIImageView() |
|||
view.translatesAutoresizingMaskIntoConstraints = false |
|||
return view |
|||
}() |
|||
|
|||
private let containerView: UIView = { |
|||
let view = UIView() |
|||
view.backgroundColor = .clear |
|||
view.translatesAutoresizingMaskIntoConstraints = false |
|||
return view |
|||
}() |
|||
|
|||
|
|||
private let titleLabel: UILabel = { |
|||
let view = UILabel() |
|||
view.font = UIFont.sanfrancisco(.bold, size: 21) |
|||
view.textColor = .themeWhite |
|||
view.translatesAutoresizingMaskIntoConstraints = false |
|||
return view |
|||
}() |
|||
|
|||
|
|||
override init(frame: CGRect) { |
|||
super.init(frame: frame) |
|||
setup() |
|||
} |
|||
|
|||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } |
|||
|
|||
private func setup(){ |
|||
containerView.backgroundColor = .blue |
|||
containerView.layer.cornerRadius = 10 |
|||
containerView.clipsToBounds = true |
|||
|
|||
contentView.addSubview(containerView) |
|||
containerView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true |
|||
containerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true |
|||
containerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16).isActive = true |
|||
containerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16).isActive = true |
|||
|
|||
containerView.addSubview(backgroundImgView) |
|||
backgroundImgView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true |
|||
backgroundImgView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true |
|||
backgroundImgView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true |
|||
backgroundImgView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true |
|||
|
|||
} |
|||
|
|||
func update(_ event: String){ |
|||
DispatchQueue.main.async { |
|||
//let imageUrl = UrlManager.sharedInstance.baseImageUrl + "/" + "\(event.imageUrl ?? "")" |
|||
self.backgroundImgView.sd_setImage(with: URL(string: event), placeholderImage: UIImage(named: "flag_bahamas")) |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,207 @@ |
|||
// |
|||
|
|||
import Foundation |
|||
import UIKit |
|||
|
|||
class HomeCarouselView: UIView { |
|||
|
|||
private var events: [String] = [] |
|||
private var timer: Timer? |
|||
|
|||
private let collectionView: UICollectionView = { |
|||
let flowlayout = UICollectionViewFlowLayout() |
|||
flowlayout.scrollDirection = .horizontal |
|||
|
|||
let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: flowlayout) |
|||
view.register(CarouselCollectionCell.self, forCellWithReuseIdentifier: CarouselCollectionCell.identifier) |
|||
view.backgroundColor = .white |
|||
view.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) |
|||
view.showsHorizontalScrollIndicator = false |
|||
view.translatesAutoresizingMaskIntoConstraints = false |
|||
view.isPagingEnabled = true |
|||
return view |
|||
}() |
|||
|
|||
private let pageControl: CustomPageControl = { |
|||
let view = CustomPageControl(frame: .zero) |
|||
view.currentPageIndicatorTintColor = .theme2E89FF |
|||
view.pageIndicatorTintColor = .themeGray2 |
|||
view.translatesAutoresizingMaskIntoConstraints = false |
|||
return view |
|||
}() |
|||
|
|||
static let identifier: String = "HomeCarouselCellID" |
|||
|
|||
/// whether or not dragging has ended |
|||
fileprivate var endDragging = false |
|||
|
|||
/// the current page |
|||
@objc open dynamic var currentIndex: Int = 0 { |
|||
didSet { |
|||
updateAccessoryViews() |
|||
} |
|||
} |
|||
|
|||
|
|||
func updateAccessoryViews() { |
|||
pageControl.currentPage = currentIndex |
|||
|
|||
} |
|||
|
|||
|
|||
override init(frame: CGRect) { |
|||
super.init(frame: .zero) |
|||
setup() |
|||
} |
|||
|
|||
required init?(coder: NSCoder) { |
|||
fatalError("init(coder:) has not been implemented") |
|||
} |
|||
|
|||
|
|||
private func setup(){ |
|||
self.backgroundColor = .themeWhite |
|||
self.addSubview(pageControl) |
|||
pageControl.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true |
|||
pageControl.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true |
|||
pageControl.heightAnchor.constraint(equalToConstant: 30).isActive = true |
|||
|
|||
self.addSubview(collectionView) |
|||
collectionView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true |
|||
collectionView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true |
|||
collectionView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true |
|||
collectionView.bottomAnchor.constraint(equalTo: pageControl.topAnchor, constant: 0).isActive = true |
|||
|
|||
collectionView.delegate = self |
|||
collectionView.dataSource = self |
|||
} |
|||
|
|||
override open func layoutSubviews() { |
|||
super.layoutSubviews() |
|||
collectionView.performBatchUpdates(nil, completion: nil) |
|||
moveToPage(currentIndex, animated: false) |
|||
} |
|||
|
|||
open func moveToPage(_ page: Int, animated: Bool) { |
|||
// outside the range |
|||
if page < 0 || page >= collectionView.numberOfItems(inSection: 0) { |
|||
return |
|||
} |
|||
|
|||
currentIndex = page |
|||
collectionView.scrollToItem(at: IndexPath(item: currentIndex, section: 0), |
|||
at: .left, animated: animated) |
|||
} |
|||
|
|||
|
|||
func update(_ events: [String]){ |
|||
self.events = events |
|||
pageControl.numberOfPages = events.count |
|||
self.collectionView.reloadData() |
|||
stopTimer() |
|||
startTimer() |
|||
} |
|||
|
|||
|
|||
/** |
|||
Scroll to Next Cell |
|||
*/ |
|||
@objc func scrollToNextCell(){ |
|||
let nextpage = self.currentIndex + 1 |
|||
if nextpage >= self.events.count{ |
|||
moveToPage(0, animated: true) |
|||
}else{ |
|||
moveToPage(nextpage, animated: true) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
Invokes Timer to start Automatic Animation with repeat enabled |
|||
*/ |
|||
private func startTimer() { |
|||
self.timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(self.scrollToNextCell), userInfo: nil, repeats: true) |
|||
} |
|||
|
|||
private func stopTimer(){ |
|||
self.timer?.invalidate() |
|||
self.timer = nil |
|||
} |
|||
|
|||
} |
|||
|
|||
//MARK:- UICollectionViewDelegate |
|||
extension HomeCarouselView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{ |
|||
|
|||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { |
|||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CarouselCollectionCell.identifier, for: indexPath) as! CarouselCollectionCell |
|||
cell.update(self.events[indexPath.row]) |
|||
return cell |
|||
} |
|||
|
|||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { |
|||
return self.events.count |
|||
} |
|||
|
|||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { |
|||
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height) |
|||
} |
|||
|
|||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { |
|||
return 0.001 |
|||
} |
|||
|
|||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { |
|||
return 0.001 |
|||
} |
|||
|
|||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { |
|||
//self.didSelect?(self.events[indexPath.row]) |
|||
} |
|||
|
|||
|
|||
/** |
|||
size of the collection view |
|||
- parameter collectionView: the collection view |
|||
- parameter collectionViewLayout: the collection view flow layout |
|||
- parameter indexPath: the index path |
|||
*/ |
|||
// public func collectionView(_ collectionView: UICollectionView, |
|||
// layout collectionViewLayout: UICollectionViewLayout, |
|||
// sizeForItemAt indexPath: IndexPath) -> CGSize { |
|||
// return collectionView.bounds.size |
|||
// } |
|||
|
|||
/** |
|||
scroll view did end dragging |
|||
- parameter scrollView: the scroll view |
|||
- parameter decelerate: wether the view is decelerating or not. |
|||
*/ |
|||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { |
|||
if !decelerate { |
|||
endScrolling(scrollView) |
|||
} else { |
|||
endDragging = true |
|||
} |
|||
} |
|||
|
|||
/** |
|||
Scroll view did end decelerating |
|||
*/ |
|||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { |
|||
if endDragging { |
|||
endDragging = false |
|||
endScrolling(scrollView) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
end scrolling |
|||
*/ |
|||
fileprivate func endScrolling(_ scrollView: UIScrollView) { |
|||
let width = scrollView.bounds.width |
|||
let page = (scrollView.contentOffset.x + (0.5 * width)) / width |
|||
currentIndex = Int(page) |
|||
self.stopTimer() |
|||
self.startTimer() |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
{ |
|||
"images" : [ |
|||
{ |
|||
"filename" : "exchangeRateButton.svg", |
|||
"idiom" : "universal", |
|||
"scale" : "1x" |
|||
}, |
|||
{ |
|||
"idiom" : "universal", |
|||
"scale" : "2x" |
|||
}, |
|||
{ |
|||
"idiom" : "universal", |
|||
"scale" : "3x" |
|||
} |
|||
], |
|||
"info" : { |
|||
"author" : "xcode", |
|||
"version" : 1 |
|||
} |
|||
} |
@ -0,0 +1,10 @@ |
|||
<svg id="_24px_43_" data-name="24px (43)" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"> |
|||
<path id="Path_1000" data-name="Path 1000" d="M0,0H20V20H0Z" fill="none"/> |
|||
<g id="icons8-up-down-arrow" transform="translate(0.799 1.587)"> |
|||
<path id="Path_38524" data-name="Path 38524" d="M.694,0A.693.693,0,0,0,.2,1.181l3.081,3.11a1,1,0,0,0,1.424,0l3.081-3.11A.693.693,0,0,0,7.3,0Z" transform="translate(10.413 12.022)"/> |
|||
<path id="Path_38525" data-name="Path 38525" d="M3.2,7.19V1.6A1.6,1.6,0,0,0,0,1.6V7.19Z" transform="translate(12.81 5.255)"/> |
|||
<circle id="Ellipse_1881" data-name="Ellipse 1881" cx="1.5" cy="1.5" r="1.5" transform="translate(12.457)"/> |
|||
<path id="Path_38526" data-name="Path 38526" d="M7.787,3.407,4.707.3A1,1,0,0,0,3.283.3L.2,3.407A.693.693,0,0,0,.694,4.588H2.4V9.381a1.6,1.6,0,0,0,3.2,0V4.588H7.3A.693.693,0,0,0,7.787,3.407Z" transform="translate(0 0.217)" opacity="0.35"/> |
|||
<circle id="Ellipse_1882" data-name="Ellipse 1882" cx="1.5" cy="1.5" r="1.5" transform="translate(2.947 13.826)" opacity="0.35"/> |
|||
</g> |
|||
</svg> |
@ -0,0 +1,130 @@ |
|||
// |
|||
|
|||
import Foundation |
|||
import UIKit |
|||
|
|||
@IBDesignable |
|||
class CustomPageControl: UIControl { |
|||
//MARK:- Properties |
|||
|
|||
private var numberOfDots = [UIView]() { |
|||
didSet{ |
|||
if numberOfDots.count == numberOfPages { |
|||
setupViews() |
|||
} |
|||
} |
|||
} |
|||
|
|||
@IBInspectable var numberOfPages: Int = 0 { |
|||
didSet{ |
|||
|
|||
for tag in 0 ..< numberOfPages { |
|||
let dot = getDotView() |
|||
dot.tag = tag |
|||
dot.backgroundColor = pageIndicatorTintColor |
|||
self.numberOfDots.append(dot) |
|||
} |
|||
} |
|||
} |
|||
|
|||
var currentPage: Int = 0 { |
|||
didSet{ |
|||
onPageControlSwipe() |
|||
} |
|||
} |
|||
|
|||
@IBInspectable var pageIndicatorTintColor: UIColor? = .blue |
|||
@IBInspectable var currentPageIndicatorTintColor: UIColor? = .green |
|||
|
|||
private lazy var stackView = UIStackView.init(frame: self.bounds) |
|||
private lazy var constantSpace = ((stackView.spacing) * CGFloat(numberOfPages - 1) + ((self.bounds.height * 0.45) * CGFloat(numberOfPages)) - self.bounds.width) |
|||
|
|||
|
|||
override var bounds: CGRect { |
|||
didSet{ |
|||
self.numberOfDots.forEach { (dot) in |
|||
self.setupDotAppearance(dot: dot) |
|||
} |
|||
} |
|||
} |
|||
|
|||
//MARK:- Intialisers |
|||
convenience init() { |
|||
self.init(frame: .zero) |
|||
} |
|||
|
|||
init(withNoOfPages pages: Int) { |
|||
self.numberOfPages = pages |
|||
self.currentPage = 0 |
|||
super.init(frame: .zero) |
|||
setupViews() |
|||
} |
|||
|
|||
override init(frame: CGRect) { |
|||
super.init(frame: frame) |
|||
setupViews() |
|||
} |
|||
|
|||
required init?(coder aDecoder: NSCoder) { |
|||
super.init(coder: aDecoder) |
|||
|
|||
} |
|||
private func setupViews() { |
|||
|
|||
self.numberOfDots.forEach { (dot) in |
|||
self.stackView.addArrangedSubview(dot) |
|||
} |
|||
|
|||
stackView.alignment = .center |
|||
stackView.axis = .horizontal |
|||
stackView.distribution = .fillEqually |
|||
stackView.spacing = 8 |
|||
stackView.translatesAutoresizingMaskIntoConstraints = false |
|||
self.addSubview(stackView) |
|||
|
|||
self.addConstraints([ |
|||
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), |
|||
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), |
|||
stackView.heightAnchor.constraint(equalTo: self.heightAnchor), |
|||
]) |
|||
|
|||
self.numberOfDots.forEach { dot in |
|||
self.addConstraints([ |
|||
dot.centerYAnchor.constraint(equalTo: self.stackView.centerYAnchor), |
|||
dot.widthAnchor.constraint(equalToConstant: 7), |
|||
dot.heightAnchor.constraint(equalToConstant: 7), |
|||
]) |
|||
} |
|||
self.numberOfDots.forEach { dot in |
|||
dot.layer.cornerRadius = dot.bounds.height / 2 |
|||
} |
|||
} |
|||
|
|||
@objc private func onPageControlSwipe() { |
|||
_ = numberOfDots.map { (dot) in |
|||
setupDotAppearance(dot: dot) |
|||
if dot.tag == currentPage { |
|||
UIView.animate(withDuration: 0.2, animations: { |
|||
dot.layer.cornerRadius = dot.bounds.height / 5 |
|||
dot.transform = CGAffineTransform.init(scaleX: 2, y: 1) |
|||
dot.backgroundColor = self.currentPageIndicatorTintColor |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
|||
//MARK: Helper methods... |
|||
private func getDotView() -> UIView { |
|||
let dot = UIView() |
|||
self.setupDotAppearance(dot: dot) |
|||
dot.translatesAutoresizingMaskIntoConstraints = false |
|||
return dot |
|||
} |
|||
|
|||
private func setupDotAppearance(dot: UIView) { |
|||
dot.transform = .identity |
|||
dot.layer.cornerRadius = dot.bounds.height / 2 |
|||
dot.layer.masksToBounds = true |
|||
dot.backgroundColor = pageIndicatorTintColor |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue