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
-
49GME Remit/Modules/Home/User Interface/View/Custom/HomeExchangeRateView.swift
-
2GME Remit/Modules/Home/User Interface/View/Home.storyboard
-
12GME 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