SwiftDeveloper SwiftDeveloper - 3 months ago 33
iOS Question

Swift 2 Multidirectional Special layout UICollectionview remote json parsing

Hello I have working special layout UIcollectionview sample but I want to change it to remote json parsing.

My CustomCollectionViewController;

class CustomCollectionViewController: UICollectionViewController {

override func viewDidLoad() {
super.viewDidLoad()

// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false

// Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

// MARK: UICollectionViewDataSource

override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
//#warning Incomplete method implementation -- Return the number of sections
return 8
}


override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
return 20
}

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomCollectionViewCell

// Configure the cell
cell.label.text = "Sec \(indexPath.section)/Item \(indexPath.item)"

//cell.label.alpha = 0
// cell.layer.borderWidth = 0.0


if indexPath.section == 0 && indexPath.item == 0 {

cell.label.text = "D"

}else if indexPath.section == 1 && indexPath.item == 0 {

cell.label.text = "E"

}else if indexPath.section == 2 && indexPath.item == 0 {

cell.label.text = "F"

}else if indexPath.section == 3 && indexPath.item == 0 {

cell.label.text = "G"

}else if indexPath.section == 4 && indexPath.item == 0 {

cell.label.text = "H"

}else if indexPath.section == 5 && indexPath.item == 0 {

cell.label.text = "I"

}else if indexPath.section == 6 && indexPath.item == 0 {

cell.label.text = "J"

}else if indexPath.section == 7 && indexPath.item == 0 {

cell.label.text = "K"

}else{


}


return cell
}


override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// You must call super to animate selection

print("Selected = \(indexPath.section)/Item \(indexPath.item)")


}


My json file;

[{
"base": "D",
"service": "D13,D18,D25"
}, {
"base": "E",
"service": "E7,E11,E18,E29,E30"
}, {
"base": "F",
"service": "F13,F18,F21,F29,F30,F40"
}, {
"base": "G",
"service": "G13,G18,G21"
}, {
"base": "H",
"service": "H13,H18,H21,H29,H30,H42"
}, {
"base": "I",
"service": "I13,I18"
}, {
"base": "J",
"service": "J13,J18,J21,J29,J30,J44"
}, {
"base": "K",
"service": "K13"
}]


Also I have sample working project on github you can download it here

https://github.com/codeservis/UICollectionviewsample

Who resolve this issue I will give him or her 100 REPS ! ( Also you can see sample view image in project github page )

Answer

I found a crash in CustomCollectionViewLayout. If return 0 from numberOfSectionsInCollectionView it crashes.

To fix this, modify prepareLayout() function of CustomCollectionViewLayout class.

Replace this line

let contentWidth = Double(collectionView!.numberOfItemsInSection(0)) * CELL_WIDTH

with this

let contentWidth = Double(maxItemInASection ?? 0) * CELL_WIDTH

If you call numberOfItemsInSection(0) when there is no numberOfSectionsInCollectionView it will crash.

maxItemInASection is the highest number of items in all sections. In the prepareLayout() where you are cycling through each item in the section. add this inside the nested for loop.

if maxItemInASection < item {
    maxItemInASection = item
}

Now the crash should be fixed. This fix is important because dataSource will be populated after the server response comes. This will take time and during that time numberOfSectins is zero.


Now make a server request and parse response. Make a struct e.g Item

struct Item {
    var base = ""
    var services = [""]
}

Add function getDataFromServer()

func getDataFromServer() {
    let url = NSURL(string: "http://www.mocky.io/v2/57a9678f110000a90b165a27")

    let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { [weak self] (data, response, error) in

        guard let strongSelf = self else { return }

        guard let data = data else { return }

        guard let jsonSerialization =  try? NSJSONSerialization.JSONObjectWithData(data, options:[]), jsonArray = jsonSerialization as? NSArray else { return }

        strongSelf.items.removeAll()

        jsonArray.forEach({ (eachItem) -> () in
            guard let dic = eachItem as? NSDictionary else { return }
            guard let service = dic["service"] as? String, base = dic["base"] as? String else { return }

            let services = service.componentsSeparatedByString(",")
            let item = Item(base: base, services: services)
            strongSelf.items.append(item)
        })

        guard let customCollectionViewLayout = strongSelf.collectionView?.collectionViewLayout as? CustomCollectionViewLayout  else { return }

        customCollectionViewLayout.dataSourceDidUpdate = true

        NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
            strongSelf.collectionView!.reloadData()
        })
    }

    task.resume()
}

Implement DataSource protocol

// MARK: UICollectionViewDataSource

override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return items.count
}


override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return  items[section].services.count + 1  // +1 because first section item is the base value
}

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomCollectionViewCell

    if indexPath.item == 0 {
        cell.label.text = items[indexPath.section].base
    } else {
        cell.label.text = items[indexPath.section].services[indexPath.item - 1]
    }

    return cell
}

Call getDataFromServer() from suitable place e.g. viewDidLoad()

getDataFromServer()

Forked your repository, made a demo project here: rishi420/UICollectionviewsample

Who resolve this issue I will give him or her 150 REPS

Rooting for 150 REPS :D

Comments