UICollectionView Drag and Drop cell between collectionView - ios

Hi I am using KDDragAndDropCollectionView for drag and drop feature between two three different collectionView. Everything is working fine, but I am not able to restrict the movement for particular case. I have three collectionView. The user can drop from A to B and B to C. But He cannot drah and drop from A to C or C to B or B to A.
Here is my code.
import SlideMenuControllerSwift
class MainViewController: NavigationBarViewController,KDDragAndDropCollectionViewDataSource {
#IBOutlet weak var inProgressView: UIView!
#IBOutlet weak var doneview: UIView!
#IBOutlet weak var toDoView: UIView!
#IBOutlet weak var doneCollectionView: UICollectionView!
#IBOutlet weak var inProgressCollectionView: UICollectionView!
#IBOutlet weak var toDoCollectionView: UICollectionView!
var drop: UIDropDown!
var toDoDataArray = [String]()
var inProgressDataArray = [String]()
var doneDataArray = [String]()
var dragAndDropManager : KDDragAndDropManager?
//MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
toDoDataArray.append("A")
toDoDataArray.append("B")
toDoDataArray.append("C")
inProgressDataArray.append("D")
doneDataArray.append("B")
doneDataArray.append("C")
// Do any additional setup after loading the view.
self.setUp()
self.setupDropDown()
self.dragAndDropManager = KDDragAndDropManager(canvas: self.view, collectionViews: [toDoCollectionView, inProgressCollectionView,doneCollectionView])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
}
//MARK: - Private Method
func setupDropDown() {
drop = UIDropDown(frame: CGRect(x: 24, y: 80, width: 200, height: 40))
//drop.center = CGPoint(x: self.view.frame.midX, y: self.view.frame.midY)
drop.placeholder = "Select Month"
drop.options = ["Weekly", "Monthly", "Bi-Annual", "Annual"]
drop.didSelect { (option, index) in
self.drop.placeholder = option
print("You just select: \(option) at index: \(index)")
}
self.view.addSubview(drop)
}
func setUp()
{
//Setup Navigation Bar
self.menuIconImage = #imageLiteral(resourceName: "hamIco")
self.setNavigationBarButtonItem()
self.setNavigationBarItem()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
//MARK: - Navigation Bar Button Action Method
//MARK: - Button Action Methods
/**
Button Action method. Gets called when the left navigation item
Parameters: sender - the button on which the event occurred
**/
#IBAction func leftBtnAction(sender: UIButton) {
self.toggleLeft()
}
// MARK: - UITableView DataSource abd Delegate
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("Step 1 called")
if collectionView.tag == 0 {
return toDoDataArray.count
}
else if collectionView.tag == 1 {
return inProgressDataArray.count
}
return doneDataArray.count
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("Step 2 called")
if collectionView.tag == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TO_DO_COLLECTION_CELL_ID, for: indexPath) as! ToDoCollectionViewCell
cell.isHidden = false
if let kdCollectionView = collectionView as? KDDragAndDropCollectionView {
if let draggingPathOfCellBeingDragged = kdCollectionView.draggingPathOfCellBeingDragged {
if draggingPathOfCellBeingDragged.item == indexPath.item {
cell.isHidden = true
}
}
}
return cell
}
else if collectionView.tag == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: IN_PROGRESS_COLLECTION_CELL_ID, for: indexPath) as! InProgressCollectionViewCell
cell.isHidden = false
if let kdCollectionView = collectionView as? KDDragAndDropCollectionView {
if let draggingPathOfCellBeingDragged = kdCollectionView.draggingPathOfCellBeingDragged {
if draggingPathOfCellBeingDragged.item == indexPath.item {
cell.isHidden = true
}
}
}
return cell
}
else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DONE_COLLECTION_Cell_ID, for: indexPath) as! DoneCollectionViewCell
cell.isHidden = false
if let kdCollectionView = collectionView as? KDDragAndDropCollectionView {
if let draggingPathOfCellBeingDragged = kdCollectionView.draggingPathOfCellBeingDragged {
if draggingPathOfCellBeingDragged.item == indexPath.item {
cell.isHidden = true
}
}
}
return cell
}
}
// MARK : KDDragAndDropCollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, dataItemForIndexPath indexPath: IndexPath) -> AnyObject {
print("Step 3 called")
if collectionView.tag == 0 {
return toDoDataArray[indexPath.item] as AnyObject
}
else if collectionView.tag == 1 {
return inProgressDataArray[indexPath.item] as AnyObject
}
return doneDataArray[indexPath.item] as AnyObject
//return data[collectionView.tag][indexPath.item]
}
func collectionView(_ collectionView: UICollectionView, insertDataItem dataItem : AnyObject, atIndexPath indexPath: IndexPath) -> Void {
print("Step 4 called")
if collectionView.tag == 0 {
if let di = dataItem as? String {
toDoDataArray.insert(di, at: indexPath.item)
//data[collectionView.tag].insert(di, at: indexPath.item)
}
}
else if collectionView.tag == 1 {
if let di = dataItem as? String {
inProgressDataArray.insert(di, at: indexPath.item)
//data[collectionView.tag].insert(di, at: indexPath.item)
}
}
else{
if let di = dataItem as? String {
doneDataArray.insert(di, at: indexPath.item)
}
}
}
func collectionView(_ collectionView: UICollectionView, deleteDataItemAtIndexPath indexPath : IndexPath) -> Void {
print("Step 5 called")
if collectionView.tag == 0 {
toDoDataArray.remove(at: indexPath.item)
}
else if collectionView.tag == 1 {
inProgressDataArray.remove(at: indexPath.item)
}
else{
doneDataArray.remove(at: indexPath.item)
}
}
func collectionView(_ collectionView: UICollectionView, moveDataItemFromIndexPath from: IndexPath, toIndexPath to : IndexPath) -> Void {
print("Step 6 called")
if collectionView.tag == 0 {
let fromDataItem: String = toDoDataArray[from.item]
toDoDataArray.remove(at: from.item)
toDoDataArray.insert(fromDataItem, at: to.item)
}
else if collectionView.tag == 1 {
let fromDataItem: String = inProgressDataArray[from.item]
inProgressDataArray.remove(at: from.item)
inProgressDataArray.insert(fromDataItem, at: to.item)
}
else{
let fromDataItem: String = doneDataArray[from.item]
doneDataArray.remove(at: from.item)
doneDataArray.insert(fromDataItem, at: to.item)
}
}
func collectionView(_ collectionView: UICollectionView, indexPathForDataItem dataItem: AnyObject) -> IndexPath? {
print("Step 7 called")
if collectionView.tag == 0 {
if let candidate : String = dataItem as? String {
for item : String in toDoDataArray {
if candidate == item {
let position = toDoDataArray.index(of: item)! // ! if we are inside the condition we are guaranteed a position
let indexPath = IndexPath(item: position, section: 0)
return indexPath
}
}
}
}
else if collectionView.tag == 1 {
if let candidate : String = dataItem as? String {
for item : String in inProgressDataArray {
if candidate == item {
let position = inProgressDataArray.index(of: item)! // ! if we are inside the condition we are guaranteed a position
let indexPath = IndexPath(item: position, section: 0)
return indexPath
}
}
}
}
else{
if let candidate : String = dataItem as? String {
for item : String in doneDataArray {
if candidate == item {
let position = doneDataArray.index(of: item)! // ! if we are inside the condition we are guaranteed a position
let indexPath = IndexPath(item: position, section: 0)
return indexPath
}
}
}
}
return nil
}
}
//MARK: - Extension written for Slide the Menu
extension MainViewController : SlideMenuControllerDelegate {
func leftWillOpen() {
print("SlideMenuControllerDelegate: leftWillOpen")
}
func leftDidOpen() {
print("SlideMenuControllerDelegate: leftDidOpen")
}
func leftWillClose() {
print("SlideMenuControllerDelegate: leftWillClose")
}
func leftDidClose() {
print("SlideMenuControllerDelegate: leftDidClose")
}
func rightWillOpen() {
print("SlideMenuControllerDelegate: rightWillOpen")
}
func rightDidOpen() {
print("SlideMenuControllerDelegate: rightDidOpen")
}
func rightWillClose() {
print("SlideMenuControllerDelegate: rightWillClose")
}
func rightDidClose() {
print("SlideMenuControllerDelegate: rightDidClose")
}
}

Related

How to track a CollectionView cell by time in Swift

I've been working on a feature to detect when a user sees a post and when he doesn't. When the user does see the post I turn the cell's background into green, when it doesn't then it stays red. Now after doing that I notice that I turn on all the cells into green even tho the user only scroll-down the page, so I added a timer but I couldn't understand how to use it right so I thought myself maybe you guys have a suggestion to me cause I'm kinda stuck with it for like two days :(
Edit: Forgot to mention that a cell marks as seen if it passes the minimum length which is 2 seconds.
Here's my Code:
My VC(CollectionView):
import UIKit
class ViewController: UIViewController,UIScrollViewDelegate {
var impressionEventStalker: ImpressionStalker?
var impressionTracker: ImpressionTracker?
var indexPathsOfCellsTurnedGreen = [IndexPath]() // All the read "posts"
#IBOutlet weak var collectionView: UICollectionView!{
didSet{
collectionView.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
impressionEventStalker = ImpressionStalker(minimumPercentageOfCell: 0.70, collectionView: collectionView, delegate: self)
}
}
func registerCollectionViewCells(){
let cellNib = UINib(nibName: CustomCollectionViewCell.nibName, bundle: nil)
collectionView.register(cellNib, forCellWithReuseIdentifier: CustomCollectionViewCell.reuseIdentifier)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
collectionView.delegate = self
collectionView.dataSource = self
registerCollectionViewCells()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
impressionEventStalker?.stalkCells()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
impressionEventStalker?.stalkCells()
}
}
// MARK: CollectionView Delegate + DataSource Methods
extension ViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let customCell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.reuseIdentifier, for: indexPath) as? CustomCollectionViewCell else {
fatalError()
}
customCell.textLabel.text = "\(indexPath.row)"
if indexPathsOfCellsTurnedGreen.contains(indexPath){
customCell.cellBackground.backgroundColor = .green
}else{
customCell.cellBackground.backgroundColor = .red
}
return customCell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: 225)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) // Setting up the padding
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//Start The Clock:
if let trackableCell = cell as? TrackableView {
trackableCell.tracker = ImpressionTracker(delegate: trackableCell)
trackableCell.tracker?.start()
}
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//Stop The Clock:
(cell as? TrackableView)?.tracker?.stop()
}
}
// MARK: - Delegate Method:
extension ViewController:ImpressionStalkerDelegate{
func sendEventForCell(atIndexPath indexPath: IndexPath) {
guard let customCell = collectionView.cellForItem(at: indexPath) as? CustomCollectionViewCell else {
return
}
customCell.cellBackground.backgroundColor = .green
indexPathsOfCellsTurnedGreen.append(indexPath) // We append all the visable Cells into an array
}
}
my ImpressionStalker:
import Foundation
import UIKit
protocol ImpressionStalkerDelegate:NSObjectProtocol {
func sendEventForCell(atIndexPath indexPath:IndexPath)
}
protocol ImpressionItem {
func getUniqueId()->String
}
class ImpressionStalker: NSObject {
//MARK: Variables & Constants
let minimumPercentageOfCell: CGFloat
weak var collectionView: UICollectionView?
static var alreadySentIdentifiers = [String]()
weak var delegate: ImpressionStalkerDelegate?
//MARK: Initializer
init(minimumPercentageOfCell: CGFloat, collectionView: UICollectionView, delegate:ImpressionStalkerDelegate ) {
self.minimumPercentageOfCell = minimumPercentageOfCell
self.collectionView = collectionView
self.delegate = delegate
}
// Checks which cell is visible:
func stalkCells() {
for cell in collectionView!.visibleCells {
if let visibleCell = cell as? UICollectionViewCell & ImpressionItem {
let visiblePercentOfCell = percentOfVisiblePart(ofCell: visibleCell, inCollectionView: collectionView!)
if visiblePercentOfCell >= minimumPercentageOfCell,!ImpressionStalker.alreadySentIdentifiers.contains(visibleCell.getUniqueId()){ // >0.70 and not seen yet then...
guard let indexPath = collectionView!.indexPath(for: visibleCell), let delegate = delegate else {
continue
}
delegate.sendEventForCell(atIndexPath: indexPath) // send the cell's index since its visible.
ImpressionStalker.alreadySentIdentifiers.append(visibleCell.getUniqueId()) // to avoid double events to show up.
}
}
}
}
// Func Which Calculate the % Of Visible of each Cell:
private func percentOfVisiblePart(ofCell cell:UICollectionViewCell, inCollectionView collectionView:UICollectionView) -> CGFloat{
guard let indexPathForCell = collectionView.indexPath(for: cell),
let layoutAttributes = collectionView.layoutAttributesForItem(at: indexPathForCell) else {
return CGFloat.leastNonzeroMagnitude
}
let cellFrameInSuper = collectionView.convert(layoutAttributes.frame, to: collectionView.superview)
let interSectionRect = cellFrameInSuper.intersection(collectionView.frame)
let percentOfIntersection: CGFloat = interSectionRect.height/cellFrameInSuper.height
return percentOfIntersection
}
}
ImpressionTracker:
import Foundation
import UIKit
protocol ViewTracker {
init(delegate: TrackableView)
func start()
func pause()
func stop()
}
final class ImpressionTracker: ViewTracker {
private weak var viewToTrack: TrackableView?
private var timer: CADisplayLink?
private var startedTimeStamp: CFTimeInterval = 0
private var endTimeStamp: CFTimeInterval = 0
init(delegate: TrackableView) {
viewToTrack = delegate
setupTimer()
}
func setupTimer() {
timer = (viewToTrack as? UIView)?.window?.screen.displayLink(withTarget: self, selector: #selector(update))
timer?.add(to: RunLoop.main, forMode: .default)
timer?.isPaused = true
}
func start() {
guard viewToTrack != nil else { return }
timer?.isPaused = false
startedTimeStamp = CACurrentMediaTime() // Current Time in seconds.
}
func pause() {
guard viewToTrack != nil else { return }
timer?.isPaused = true
endTimeStamp = CACurrentMediaTime()
print("Im paused!")
}
func stop() {
timer?.isPaused = true
timer?.invalidate()
}
#objc func update() {
guard let viewToTrack = viewToTrack else {
stop()
return
}
guard viewToTrack.precondition() else {
startedTimeStamp = 0
endTimeStamp = 0
return
}
endTimeStamp = CACurrentMediaTime()
trackIfThresholdCrossed()
}
private func trackIfThresholdCrossed() {
guard let viewToTrack = viewToTrack else { return }
let elapsedTime = endTimeStamp - startedTimeStamp
if elapsedTime >= viewToTrack.thresholdTimeInSeconds() {
viewToTrack.viewDidStayOnViewPortForARound()
startedTimeStamp = endTimeStamp
}
}
}
my customCell:
import UIKit
protocol TrackableView: NSObject {
var tracker: ViewTracker? { get set }
func thresholdTimeInSeconds() -> Double //Takes care of the screen's time, how much "second" counts.
func viewDidStayOnViewPortForARound() // Counter for how long the "Post" stays on screen.
func precondition() -> Bool // Checks if the View is full displayed so the counter can go on fire.
}
class CustomCollectionViewCell: UICollectionViewCell {
var tracker: ViewTracker?
static let nibName = "CustomCollectionViewCell"
static let reuseIdentifier = "customCell"
#IBOutlet weak var cellBackground: UIView!
#IBOutlet weak var textLabel: UILabel!
var numberOfTimesTracked : Int = 0 {
didSet {
self.textLabel.text = "\(numberOfTimesTracked)"
}
}
override func awakeFromNib() {
super.awakeFromNib()
cellBackground.backgroundColor = .red
layer.borderWidth = 0.5
layer.borderColor = UIColor.lightGray.cgColor
}
override func prepareForReuse() {
super.prepareForReuse()
print("Hello")
tracker?.stop()
tracker = nil
}
}
extension CustomCollectionViewCell: ImpressionItem{
func getUniqueId() -> String {
return self.textLabel.text!
}
}
extension CustomCollectionViewCell: TrackableView {
func thresholdTimeInSeconds() -> Double { // every 2 seconds counts as a view.
return 2
}
func viewDidStayOnViewPortForARound() {
numberOfTimesTracked += 1 // counts for how long the view stays on screen.
}
func precondition() -> Bool {
let screenRect = UIScreen.main.bounds
let viewRect = convert(bounds, to: nil)
let intersection = screenRect.intersection(viewRect)
return intersection.height == bounds.height && intersection.width == bounds.width
}
}
The approach you probably want to use...
In you posted code, you've created an array of "read posts":
var indexPathsOfCellsTurnedGreen = [IndexPath]() // All the read "posts"
Assuming your real data will have multiple properties, such as:
struct TrackPost {
var message: String = ""
var postAuthor: String = ""
var postDate: Date = Date()
// ... other stuff
}
add another property to track whether or not it has been "seen":
struct TrackPost {
var message: String = ""
var postAuthor: String = ""
var postDate: Date = Date()
// ... other stuff
var hasBeenSeen: Bool = false
}
Move all of your "tracking" code out of the controller, and instead add a Timer to your cell class.
When the cell appears:
if hasBeenSeen for that cell's Data is false
start a 2-second timer
if the timer elapses, the cell has been visible for 2 seconds, so set hasBeenSeen to true (use a closure or protocol / delegate pattern to tell the controller to update the data source) and change the backgroundColor
if the cell is scrolled off-screen before the timer elapses, stop the timer and don't tell the controller anything
if hasBeenSeen is true to begin with, don't start the 2-second timer
Now, your cellForItemAt code will look something like this:
let p: TrackPost = myData[indexPath.row]
customCell.authorLabel.text = p.postAuthor
customCell.dateLabel.text = myDateFormat(p.postDate) // formatted as a string
customCell.textLabel.text = p.message
// setting hasBeenSeen in your cell should also set the backgroundColor
// and will be used so the cell knows whether or not to start the timer
customCell.hasBeenSeen = p.hasBeenSeen
// this will be called by the cell if the timer elapsed
customCell.wasSeenCallback = { [weak self] in
guard let self = self else { return }
self.myData[indexPath.item].hasBeenSeen = true
}
What about a much simpler approach like:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
for subview in collectionView!.visibleCells {
if /* check visible percentage */ {
if !(subview as! TrackableCollectionViewCell).timerRunning {
(subview as! TrackableCollectionViewCell).startTimer()
}
} else {
if (subview as! TrackableCollectionViewCell).timerRunning {
(subview as! TrackableCollectionViewCell).stopTimer()
}
}
}
}
With a Cell-Class extended by:
class TrackableCollectionViewCell {
static let minimumVisibleTime: TimeInterval = 2.0
var timerRunning: Bool = true
private var timer: Timer = Timer()
func startTimer() {
if timerRunning {
return
}
timerRunning = true
timer = Timer.scheduledTimer(withTimeInterval: minimumVisibleTime, repeats: false) { (_) in
// mark cell as seen
}
}
func stopTimer() {
timerRunning = false
timer.invalidate()
}
}

Questions about removing ios photo asset in Swift

This source code is a photo app like the iPhone's photo app.
When you launch the app, each Asset that is a CollectionViewCell is shown.
What I would like to ask is the ability to select and delete an image asset. If you look at the iPhone photo app, you can press the select button to select photos and delete and share selected photos. You can choose as many photos as you want rather than just one. I have implemented #IBAction selectButtonPressed.
class PhotoCollectionViewController: UICollectionViewController {
#IBOutlet weak var sortButton: UIBarButtonItem!
#IBOutlet weak var selectButton: UIBarButtonItem!
#IBOutlet weak var actionButton: UIBarButtonItem!
#IBOutlet weak var trashButton: UIBarButtonItem!
// MARK:- Properties
var fetchResult: PHFetchResult<PHAsset>? {
didSet {
OperationQueue.main.addOperation {
self.collectionView?.reloadSections(IndexSet(0...0))
}
}
}
var assetCollection: PHAssetCollection?
// MARK:- Privates
private let cellReuseIdentifier: String = "photoCell"
private lazy var cachingImageManager: PHCachingImageManager = {
return PHCachingImageManager()
}()
// MARK:- Life Cycle
deinit {
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
#IBAction func sortButtonPressed(_ sender: UIBarButtonItem) {
let fetchOptions: PHFetchOptions = PHFetchOptions()
if (self.sortButton.title == "In the past") {
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "modificationDate",
ascending: false)]
self.fetchResult = PHAsset.fetchAssets(in: assetCollection!, options: fetchOptions )
self.sortButton.title = "The latest"
} else if (self.sortButton.title == "The latest") {
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: true)]
self.fetchResult = PHAsset.fetchAssets(in: assetCollection!, options: fetchOptions )
self.sortButton.title = "In the past"
}
}
#IBAction func seletButtonPressed(_ sender: Any) {
if (self.sortButton.isEnabled == true) {
self.sortButton.isEnabled = false
self.actionButton.isEnabled = true
self.trashButton.isEnabled = true
} else if (self.sortButton.isEnabled == false) {
self.sortButton.isEnabled = true
self.actionButton.isEnabled = false
self.trashButton.isEnabled = false
}
PHPhotoLibrary.shared().performChanges({
//Delete Photo
PHAssetChangeRequest.deleteAssets(self.fetchResult!)
},
completionHandler: {(success, error)in
NSLog("\nDeleted Image -> %#", (success ? "Success":"Error!"))
if(success){
}else{
print("Error: \(error)")
}
})
}
}
extension PhotoCollectionViewController {
private func configureCell(_ cell: PhotoCollectionViewCell,
collectionView: UICollectionView,
indexPath: IndexPath) {
guard let asset: PHAsset = self.fetchResult?.object(at: indexPath.item) else { return }
let manager: PHCachingImageManager = self.cachingImageManager
let handler: (UIImage?, [AnyHashable:Any]?) -> Void = { image, _ in
let cellAtIndex: UICollectionViewCell? = collectionView.cellForItem(at: indexPath)
guard let cell: PhotoCollectionViewCell = cellAtIndex as? PhotoCollectionViewCell
else { return }
cell.imageView.image = image
}
manager.requestImage(for: asset,
targetSize: CGSize(width: 100, height: 100),
contentMode: PHImageContentMode.aspectFill,
options: nil,
resultHandler: handler)
}
}
// MARK:- UICollectionViewDataSource
extension PhotoCollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return self.fetchResult?.count ?? 0
}
}
extension PhotoCollectionViewController {
override func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PhotoCollectionViewCell
cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellReuseIdentifier,
for: indexPath) as! PhotoCollectionViewCell
return cell
}
override func collectionView(_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell,
forItemAt indexPath: IndexPath) {
guard let cell: PhotoCollectionViewCell = cell as? PhotoCollectionViewCell else {
return
}
self.configureCell(cell, collectionView: collectionView, indexPath: indexPath)
}
}
// MARK:- UICollectionViewDelegateFlowLayout
extension PhotoCollectionViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
guard let flowLayout: UICollectionViewFlowLayout =
self.collectionViewLayout as? UICollectionViewFlowLayout else { return CGSize.zero}
let numberOfCellsInRow: CGFloat = 4
let viewSize: CGSize = self.view.frame.size
let sectionInset: UIEdgeInsets = flowLayout.sectionInset
let interitemSpace: CGFloat = flowLayout.minimumInteritemSpacing * (numberOfCellsInRow - 1)
var itemWidth: CGFloat
itemWidth = viewSize.width - sectionInset.left - sectionInset.right - interitemSpace
itemWidth /= numberOfCellsInRow
let itemSize = CGSize(width: itemWidth, height: itemWidth)
return itemSize
}
}
extension PhotoCollectionViewController {
private func updateCollectionView(with changes: PHFetchResultChangeDetails<PHAsset>) {
guard let collectionView = self.collectionView else { return }
// 업데이트는 삭제, 삽입, 다시 불러오기, 이동 순으로 진행합니다
if let removed: IndexSet = changes.removedIndexes, removed.count > 0 {
collectionView.deleteItems(at: removed.map({
IndexPath(item: $0, section: 0)
}))
}
if let inserted: IndexSet = changes.insertedIndexes, inserted.count > 0 {
collectionView.insertItems(at: inserted.map({
IndexPath(item: $0, section: 0)
}))
}
if let changed: IndexSet = changes.changedIndexes, changed.count > 0 {
collectionView.reloadItems(at: changed.map({
IndexPath(item: $0, section: 0)
}))
}
changes.enumerateMoves { fromIndex, toIndex in
collectionView.moveItem(at: IndexPath(item: fromIndex, section: 0),
to: IndexPath(item: toIndex, section: 0))
}
}
}
// MARK:- PHPhotoLibraryChangeObserver
extension PhotoCollectionViewController: PHPhotoLibraryChangeObserver {
private func resetCachedAssets() {
self.cachingImageManager.stopCachingImagesForAllAssets()
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let fetchResult: PHFetchResult<PHAsset> = self.fetchResult
else { return }
guard let changes: PHFetchResultChangeDetails<PHAsset> =
changeInstance.changeDetails(for: fetchResult)
else { return }
DispatchQueue.main.sync {
self.resetCachedAssets()
self.fetchResult = changes.fetchResultAfterChanges
if changes.hasIncrementalChanges {
self.updateCollectionView(with: changes)
} else {
self.collectionView?.reloadSections(IndexSet(0...0))
}
}
}
}
extension PhotoCollectionViewController {
// MARK:- Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
PHPhotoLibrary.shared().register(self)
self.sortButton.title = "In the past"
self.actionButton.isEnabled = false
self.trashButton.isEnabled = false
}
}
AssetCollection can be selected by clicking the select barButtonItem in the upper right corner of the screen, and I want to delete or share selected pictures.
You can perfore delete action on phAsset as below:
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
//Delete Photo
PHAssetChangeRequest.deleteAssets(delShotsAsset)
},
completionHandler: {(success, error)in
NSLog("\nDeleted Image -> %#", (success ? "Success":"Error!"))
if(success){
}else{
println("Error: \(error)")
}
})

Fetching JSON to ViewController and segue to 2nd ViewController

I have fetched and parsed my Data from API in JSON format to FirstViewController and want to segue to the SecondViewController with the data selected at the concrete person from FirstViewController. The Problem is that I have an API with such example URL: https://www.example.com/api/?action=persons&ln=en which gives me all persons in this format:
[
{
"p_id": "4107",
"p_name": "Name1 Surname1",
"p_role": "Role1",
"general_image": "/imagedb/persons/4107/main/1.jpg"
},{
"p_id": "1978",
"p_name": "Name2 Surname2",
"p_role": "Role2",
"general_image": "/imagedb/persons/1978/main/1.jpg"
}, {...
...}
]
I am showing all these persons in my FirstViewController in CollectionView, which is working correctly, with images, names, roles. But also I need to show my SecondViewController with the data selected in FirstVC. My API for personByID is like this URL: https://www.example.com/api/?action=person&ln=en&personId=1978 which gives me JSON Data in this format:
{
"p_id": "1978",
"p_category": "[2]",
"p_name": "Name2 Surname2",
"p_role": "Role2",
"p_short": null,
"p_text": "long text...",
"p_date_start": "1922.02.05",
"p_date_end": "",
"p_profile_image": "1",
"p_status": "1",
"p_lang": "en",
"general_image": "/imagedb/persons/1978/main/1.jpg",
"photos": [
{
"image_id": "5",
"p_id": "1978",
"lang": "en",
"text": "some text...",
"general": "/imagedb/persons/1978/5.jpg",
"thumbs": "/imagedb/persons/1978/thumb/5.jpg"
},
{
"image_id": "7",
"p_id": "1978",
"lang": "en",
"text": "some text...",
"general": "/imagedb/persons/1978/7.jpg",
"thumbs": "/imagedb/persons/1978/thumb/7.jpg"
}
]
}
This is my Person Struct:
struct Person {
let id: String
let name: String
let role: String
fileprivate let imageURLString: String
var imageURL: URL? {
return URL(string: "https://www.example.com\(imageURLString)")
}
}
extension Person: JSONDecodable {
init(_ decoder: JSONDecoder) throws {
self.id = try decoder.value(forKey: "p_id")
self.name = try decoder.value(forKey: "p_name")
self.role = try decoder.value(forKey: "p_role")
self.imageURLString = try decoder.value(forKey: "general_image")
}
}
This is My FIRST VC:
import UIKit
class PersonListViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var personPages: [PagedResult<Person>] = [] {
didSet {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadPersons()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first else {
return
}
collectionView.deselectItem(at: selectedIndexPath, animated: animated)
}
var service = ExampleWebService()
private func loadPersons(page: Int = 0, resultsPerPage: Int = 5) {
service.persons(page: page, resultsPerPage: resultsPerPage) { (personPage) in
guard !self.loadedPersonPageNumbers.contains(page) else { return }
self.personPages.append(personPage)
self.updateLastIndexPath(personPage)
}
}
private(set) var lastIndexPath: IndexPath?
private func updateLastIndexPath(_ personPage: PagedResult<Person>) {
if personPage.results.isEmpty {
lastIndexPath = nil
}
else {
lastIndexPath = calculateLastIndexPath()
}
}
private func calculateLastIndexPath() -> IndexPath? {
guard let lastPage = personPages.last else { return nil }
let section = lastPage.pageNumber
let row = lastPage.results.count - 1
return IndexPath(row: row, section: section)
}
fileprivate var loadedPersonPageNumbers: [Int] {
return personPages.map { $0.pageNumber }
}
func person(at indexPath: IndexPath) -> Person? {
guard indexPath.section < personPages.count else {
return nil
}
guard indexPath.row < personPages[indexPath.section].results.count else {
return nil
}
let page = personPages[indexPath.section]
return page.results[indexPath.row]
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let personViewController = segue.destination as? PersonViewController,
let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first else {
return
}
personViewController.person = person(at: selectedIndexPath)
}
#IBAction func exitToPersonsView(segue: UIStoryboardSegue) {
}
}
extension PersonListViewController: UICollectionViewDelegate {
fileprivate var nextPageIndex: Int {
guard let lastPage = personPages.last else {
return 0
}
return lastPage.pageNumber.advanced(by: 1)
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath == lastIndexPath {
loadPersons(page: nextPageIndex)
}
}
}
extension PersonListViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return personPages.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return personPages[section].results.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PersonListCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! PersonListCollectionViewCell
cell.person = person(at: indexPath)
return cell
}
}
extension PersonListViewController: UINavigationBarDelegate {
func position(for bar: UIBarPositioning) -> UIBarPosition {
return .topAttached
}
}
And this is My Second VC:
import Foundation
import UIKit
final class PersonViewController: UIViewController {
#IBOutlet weak var imagesCollectionVIew: UICollectionView!
#IBOutlet weak var personRole: UILabel!
#IBOutlet weak var customNavigationBar: UINavigationBar!
var personImagesByID: [PagedResult<Person>] = [] {
didSet {
DispatchQueue.main.async {
self.imagesCollectionVIew.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadPersonImagesByID()
}
var person: Person?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let person = person {
title = person.name
personRole.text = person.role
}
customNavigationBar.topItem?.title = title
}
var service = ExampleWebService()
private func loadPersonImagesByID(page: Int = 0, resultsperPge: Int = 5) {
service.persons(page: page, resultsPerPage: resultsperPge) { (personPage) in
guard !self.loadedPersonPageNumbers.contains(page) else {
return
}
self.personImagesByID.append(personPage)
self.updateLastIndexPath(personPage)
}
}
private(set) var lastIndexPath: IndexPath?
private func updateLastIndexPath(_ personPage: PagedResult<Person>) {
if personPage.results.isEmpty {
lastIndexPath = nil
}
else {
lastIndexPath = calculateLastIndexPath()
}
}
private func calculateLastIndexPath() -> IndexPath? {
guard let lastPage = personImagesByID.last else {
return nil
}
let section = lastPage.pageNumber
let item = lastPage.results.count - 1
return IndexPath(row: item, section: section)
}
fileprivate var loadedPersonPageNumbers: [Int] {
return personImagesByID.map { $0.pageNumber }
}
func person(at indexPath: IndexPath) -> Person? {
guard indexPath.section < personImagesByID.count else {
return nil
}
guard indexPath.item < personImagesByID[indexPath.section].results.count else {
return nil
}
let page = personImagesByID[indexPath.section]
return page.results[indexPath.item]
}
}
extension PersonViewController: UICollectionViewDelegate {
fileprivate var nextPageIndex: Int {
guard let lastPage = personImagesByID.last else {
return 0
}
return lastPage.pageNumber.advanced(by: 1)
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath == lastIndexPath {
loadPersonImagesByID(page: nextPageIndex)
}
}
}
extension PersonViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.imagesCollectionVIew.frame.height - 17, height: self.imagesCollectionVIew.frame.height - 17)
}
}
extension PersonViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return personImagesByID.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return personImagesByID[section].results.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PersonViewCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImagesCollectionViewCell", for: indexPath) as! PersonViewCollectionViewCell
cell.person = person(at: indexPath)
return cell
}
}
extension PersonViewController: UINavigationBarDelegate {
func position(for bar: UIBarPositioning) -> UIBarPosition {
return .topAttached
}
}
Now my issue is that I am having a problem of how to write correctly my second struct PersonByID and when clicking on the person from First VC to show me data in Second VC from personByID URL Path.

How to add a custom view after every 2 rows in a collectionView swift

I am implementing general collectionView in a viewController to populate data and the collection view has 2 columns and the number of rows depend on the data, and now my collectionView looks like this.
Normal collectionView:
This is what I have implemented in my app as you can see it is a normal collection view with n rows and 2 columns. But, our requirement is
Business requirement Image:
There is the custom view which is added after every 2 rows and it is static with a two labels and a button...
I don't know if it is possible and how to achieve this... And after searching for some time I learned that we can do this by using DecoratorViews and I don't know what are those and how to use them.. If anyone have any idea on how to achieve this kind of layout, please guide me..
variables:
let columnsPerRow = 2
let addAfterRows = 5
var cellToShowWithAdds = 0
Function:
func getCategoryProducts() {
var id = Int()
var categoryProductsAPI = ""
if self.brandId != nil {
id = self.brandId!
if self.selectedSubCategoryId != nil {
categoryProductsAPI = "\(API.CATEGORY_BRAND_FILTER)\(self.selectedSubCategoryId!)\(API.BRAND_ID )\(id)"
} else {
categoryProductsAPI = "\(API.CATEGORY_BRAND_FILTER)\(self.categoryId!)\(API.BRAND_ID )\(id)"
}
} else {
if self.selectedSubCategoryId != nil {
id = self.selectedSubCategoryId!
} else {
id = self.categoryId!
}
categoryProductsAPI = "\(API.CATEGORY_PRODUCTS)\(id)"
}
print(categoryProductsAPI)
self.cellToShowWithAdds = 0
self.categoryProductsData = []
self.loadingView.isHidden = false
self.loadingActivityIndicator.animate()
ServiceManager.callGetAPI(url: categoryProductsAPI, view: self, closure: { response in
self.loadingView.isHidden = true
self.loadingActivityIndicator.stopAnimating()
guard let categoryData = response?.result.value else {return}
if let categories = categoryData as? [[String : Any]] {
for product in categories {
let productName = product["product_name"] as! String
let productId = product["product_id"] as! String
let productBrand = product["product_brand"] as! String
guard let productOffPercent = product["product_sale_of"] else { return }
let productImage = product["product_image"] as! String
let productPrice = product["product_price"] as! String
let productSepcialPrice = product["product_special_price"] as! String
var newProductPrice = String()
if productSepcialPrice == "Rs.0.00" {
newProductPrice = productPrice
} else {
newProductPrice = productSepcialPrice
}
self.categoryProductsData.append(ProductDetails(productID: productId, productName: productName, productPrice: productPrice, productSpecialPrice: newProductPrice, productOff: productOffPercent, productBrand: productBrand, productImageURL: productImage))
}
let quot = (self.categoryProductsData.count/(self.columnsPerRow * self.addAfterRows))
self.cellToShowWithAdds = self.categoryProductsData.count + quot + 1
DispatchQueue.main.async {
self.categoryProductsCollection.reloadData()
}
}
}, errorAction: {
self.loadingView.isHidden = true
self.loadingActivityIndicator.stopAnimating()
}, okAction: {
self.view.setNeedsLayout()
self.viewWillAppear(true)
})
}
DataSource methods:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cellToShowWithAdds
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row % 5 != 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "productCell", for: indexPath) as! ProductDisplayCell
let productId = Int(categoryProductsData[indexPath.item].productID)
cell.tag = productId!
if categoryProductsData[indexPath.item].productImageURL != "" {
if let productImage = URL(string: categoryProductsData[indexPath.item].productImageURL) {
cell.productImage.getImageWith(imageUrl: productImage)
}
} else {
cell.productImage.image = nil
}
cell.productNameLabel.text = categoryProductsData[indexPath.item].productName
cell.sellerNameLabel.text = categoryProductsData[indexPath.item].productBrand
cell.offerPercentLabel.text = "\(categoryProductsData[indexPath.item].productOff)% Off"
if "\(categoryProductsData[indexPath.item].productOff)" == "" || "\(categoryProductsData[indexPath.item].productOff)" == "100" || "\(categoryProductsData[indexPath.item].productOff)" == "0" {
cell.offerPercentLabel.isHidden = true
} else {
cell.offerPercentLabel.isHidden = false
}
if categoryProductsData[indexPath.item].productSpecialPrice != "Rs.0.00" {
if categoryProductsData[indexPath.item].productPrice == categoryProductsData[indexPath.item].productSpecialPrice {
cell.originalPriceLable.isHidden = true
cell.offerPriceLabel.isHidden = false
} else {
cell.originalPriceLable.isHidden = false
cell.offerPriceLabel.isHidden = false
}
} else if categoryProductsData[indexPath.item].productSpecialPrice == "Rs.0.00" {
cell.originalPriceLable.isHidden = true
cell.offerPriceLabel.isHidden = true
} else {
cell.originalPriceLable.isHidden = false
cell.offerPriceLabel.isHidden = false
}
cell.originalPriceLable.attributedText = categoryProductsData[indexPath.item].productPrice.strikeThrough()
cell.offerPriceLabel.text = categoryProductsData[indexPath.item].productSpecialPrice
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "formCollectionCell", for: indexPath) as! PostRequirementCellCollectionViewCell
return cell
}
}
My Code should be explanatory. I have set some values in viewdidload to get the kind of view you require.
import UIKit
class CollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView : UICollectionView!
let totalProducts = 21
let columnsPerRow = 2
let addAfterRows = 2
var celltoShowWithAds = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let quot = (totalProducts/(columnsPerRow * addAfterRows))
print(quot)
celltoShowWithAds = totalProducts + quot + 1
collectionView.register(UINib(nibName: "CollectionItemCell", bundle: nil), forCellWithReuseIdentifier: "CollectionItemCell")
collectionView.register(UINib(nibName: "CollectionAdvertisementCell", bundle: nil), forCellWithReuseIdentifier: "CollectionAdvertisementCell")
collectionView.delegate = self
collectionView.dataSource = self
collectionView.reloadData()
//collectionView.backgroundColor = UIColor.blue
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return celltoShowWithAds
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 0{
let myCell:CollectionAdvertisementCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionAdvertisementCell", for: indexPath) as! CollectionAdvertisementCell
return myCell as CollectionAdvertisementCell;
}else if indexPath.row % 5 == 0{
let myCell:CollectionAdvertisementCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionAdvertisementCell", for: indexPath) as! CollectionAdvertisementCell
return myCell as CollectionAdvertisementCell;
}else{
let myCell:CollectionItemCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionItemCell", for: indexPath) as! CollectionItemCell
return myCell as CollectionItemCell;
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.row == 0{
return CGSize(width: view.frame.width, height: 0.0)
}else if indexPath.row % 5 == 0 {
return CGSize(width: view.frame.width, height: 80.0)
}else{
return CGSize(width: view.frame.width/CGFloat(columnsPerRow), height: 200.0)
}
}
//Use for interspacing
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}

How to reload UIPageViewController to reload its views in Swift

I am using Page Controller embeded in Table VC. Table VC shows details of items and also contains collection view controller as embedded
one. So now when I select any Collection cell it should display the selected cell item details.
I am able to show everything for the new selected item but Page VC is not getting reloaded as per selected item images, it is still showing the last item images.
SO I'm stuck there. I am attaching the code for Page VC and Detail Table View
Please let me know the approach here to deal with it. Thanks in Advance.!!
//-----PAGE VC CODE------
import UIKit
import Firebase
protocol ProductImagesPageVCDelegate: class
{
func setupPageController(numberOfPages: Int)
func turnPageController(to index: Int)
}
class ProductImagesPageVC: UIPageViewController {
var product: Product!
weak var pageViewControllerDelegate: ProductImagesPageVCDelegate?
struct StoryBoard {
static let productImageVC = "ProductImageVC"
}
lazy var controllers: [UIViewController] = {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var controllers = [UIViewController]()
if let imageLinks = self.product.imageLinks
{
for imageLink in imageLinks
{
let productImageVC = storyboard.instantiateViewController(withIdentifier: StoryBoard.productImageVC)
controllers.append(productImageVC)
}
}
self.pageViewControllerDelegate?.setupPageController(numberOfPages: controllers.count)
return controllers
}()
override func viewDidLoad() {
super.viewDidLoad()
// if #available(iOS 11.0, *) {
// contentInsetAdjustmentBehavior = .never
// } else {
// automaticallyAdjustsScrollViewInsets = false
// }
automaticallyAdjustsScrollViewInsets = false
dataSource = self
delegate = self
self.turnToPage(index: 0)
}
func turnToPage(index: Int)
{
let controller = controllers[index]
var direction = UIPageViewControllerNavigationDirection.forward
if let currentVC = viewControllers?.first
{
guard let currentIndex = controllers.index(of: currentVC) else {return}
if currentIndex > index
{
direction = .reverse
}
}
self.configuewDisplaying(viewController: controller)
setViewControllers([controller], direction: direction, animated: true, completion: nil)
}
func configuewDisplaying(viewController: UIViewController)
{
for (index, vc) in controllers.enumerated()
{
if viewController === vc {
if let productImageVC = viewController as? ProductImageVC
{
productImageVC.imageLink = self.product.imageLinks?[index]
self.pageViewControllerDelegate?.turnPageController(to: index)
}
}
}
}
}
extension ProductImagesPageVC: UIPageViewControllerDataSource
{
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if let index = controllers.index(of: viewController)
{
if index < controllers.count - 1
{
return controllers[index + 1]
}
}
return controllers.first
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let index = controllers.index(of: viewController)
{
if index > 0
{
return controllers[index - 1]
}
}
return controllers.last
}
}
extension ProductImagesPageVC: UIPageViewControllerDelegate
{
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
self.configuewDisplaying(viewController: pendingViewControllers.first as! ProductImageVC)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if !completed
{
self.configuewDisplaying(viewController: previousViewControllers.first as! ProductImageVC)
}
}
}
//-----------Tabel VIEW Controllers-----
import UIKit
class ProductDetailTVC: UITableViewController {
#IBOutlet var productImagesHeaderView: ProductImagesHeaderView!
var product: Product!
var products: [Product]?
private var selectedProduct: Product?
struct Storyboard {
static let productDetailCell = "ProductDetailCell"
static let buyButtonCell = "BuyButtonCell"
static let showProductDetailCell = "ShowProductDetailCell"
static let suggestionTableCell = "SuggestionTableCell"
static let showImagesPageVC = "ShowProductImagesPageVC"
static let showProductDetail = "ShowProductDetail"
}
override func viewDidLoad() {
super.viewDidLoad()
title = product.name
fetchProducts()
tableView.estimatedRowHeight = tableView.rowHeight
tableView.rowHeight = UITableViewAutomaticDimension
}
func fetchProducts()
{
Product.fetchProducts { (products) in
self.products = products
if let index = self.products?.index(where: {$0 === self.product}) {
self.products?.remove(at: index)
}
self.tableView.reloadData()
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 4
}
fileprivate func extractedFunc() -> UITableViewCell {
return UITableViewCell()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0
{
let cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.productDetailCell, for: indexPath) as! ProductDetailCell
cell.product = product
cell.selectionStyle = .none
return cell
} else if indexPath.row == 1
{
let cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.buyButtonCell, for: indexPath) as! BuyButtonCell
cell.product = product
cell.selectionStyle = .none
return cell
} else if indexPath.row == 2
{
let cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.showProductDetailCell, for: indexPath)
cell.selectionStyle = .none
return cell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.suggestionTableCell, for: indexPath) as! SuggestionTableCell
//cell.selectionStyle = .none
return cell
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 3
{
return tableView.bounds.width + 68
}
else
{
return UITableViewAutomaticDimension
}
}
//Mark: - UITabeleViewDelegate
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == 3
{
if let cell = cell as? SuggestionTableCell
{
cell.collectionView.delegate = self
cell.collectionView.dataSource = self
cell.collectionView.reloadData()
cell.collectionView.isScrollEnabled = false
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Storyboard.showImagesPageVC
{
if let imagesPageVC = segue.destination as? ProductImagesPageVC
{
imagesPageVC.product = product
imagesPageVC.pageViewControllerDelegate = productImagesHeaderView
}
}
}
}
//MARK: - UICollectionViewDataSource
extension ProductDetailTVC : UICollectionViewDataSource
{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SuggestionCollectionViewCell", for: indexPath) as! SuggestionCollectionViewCell
guard let products = products else {return cell}
let randomProduct = Int(arc4random_uniform(UInt32(products.count)))
cell.product = products[randomProduct]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let selectedProduct = products?[indexPath.item] else {return}
self.selectedProduct = selectedProduct
self.product = selectedProduct
navigationItem.title = selectedProduct.name
self.tableView.reloadData()
collectionView.reloadData()
self.reloadInputViews()
}
}
//MARK: - UICollectionViewDelegate
extension ProductDetailTVC : UICollectionViewDelegate
{
}
//MARK: - UICOLLECTIONVIEWDELEGATEFLOWLAYOUT
extension ProductDetailTVC : UICollectionViewDelegateFlowLayout
{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if let layout = collectionViewLayout as? UICollectionViewFlowLayout
{
layout.minimumLineSpacing = 5.0
layout.minimumInteritemSpacing = 2.5
let itemWidth = (collectionView.bounds.width - 5.0) / 2.0
return CGSize(width: itemWidth, height: itemWidth)
}
return CGSize.zero
}
}
I assume you want it to change when you update the product property?
You'll need to add a didSet to the property which updates the currently displayed view controller.

Resources