I'm working on an onboarding flow for my iOS App in Swift. I'd like to allow users to tap other users in a collection view and have it follow those users. I need the collection view to be able to allow multiple cells to be selected, store the cells in an array and run a function once the users taps the next button. Here's my controller code:
class FollowUsers: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var tableData: [SwiftyJSON.JSON] = []
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var loadingView: UIView!
private var selectedUsers: [SwiftyJSON.JSON] = []
override func viewDidLoad() {
super.viewDidLoad()
self.getCommunities()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getUsers() {
Alamofire.request(.GET, "url", parameters: parameters)
.responseJSON {response in
if let json = response.result.value {
let jsonObj = SwiftyJSON.JSON(json)
if let data = jsonObj.arrayValue as [SwiftyJSON.JSON]? {
self.tableData = data
self.collectionView.reloadData()
self.loadingView.hidden = true
}
}
}
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.tableData.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: UserViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("userCell", forIndexPath: indexPath) as! UserViewCell
let rowData = tableData[indexPath.row]
if let userName = rowData["name"].string {
cell.userName.text = userName
}
if let userAvatar = rowData["background"].string {
let url = NSURL(string: userAvatar)
cell.userAvatar.clipsToBounds = true
cell.userAvatar.contentMode = .ScaleAspectFill
cell.userAvatar.hnk_setImageFromURL(url!)
}
cell.backgroundColor = UIColor.whiteColor()
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell: UserViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("userCell", forIndexPath: indexPath) as! UserViewCell
let rowData = tableData[indexPath.row]
let userName = rowData["name"].string
let userId = rowData["id"].int
selectedUsers.append(rowData[indexPath.row])
print("Cell \(userId) \(userName) selected")
}
}
override func viewDidLoad() {
super.viewDidLoad()
collection.dataSource = self
collection.delegate = self
collection.allowsMultipleSelection = true
self.getCommunities()
}
You should be able to make multiple selections with this.
Related
I am facing a problem in my application. When I run my application in the emulator, I don't get any data and just a white screen.
I decided to use Firestore as a backend. Below I provide the code and hope you can help me.
ViewController
class ViewController: UIViewController {
#IBOutlet weak var cv: UICollectionView!
var channel = [Channel]()
override func viewDidLoad() {
super.viewDidLoad()
self.cv.delegate = self
self.cv.dataSource = self
let db = Firestore.firestore()
db.collection("content").getDocuments() {( quarySnapshot, err) in
if let err = err {
print("error")
} else {
for document in quarySnapshot!.documents {
if let name = document.data()["title"] as? Channel {
self.channel.append(name)
}
}
self.cv.reloadData()
}
}
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return channel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ContentCell
let indexChannel = channel[indexPath.row]
cell.setup(channel: indexChannel)
return cell
}
}
This is my cell
class ContentCell: UICollectionViewCell {
#IBOutlet weak var channelText: UILabel!
#IBOutlet weak var subtitle: UITextView!
func setup(channel: Channel) {
channelText.text = channel.title
subtitle.text = channel.subtitle
}
}
If you need additional information - write
Your problem is here
if let name = document.data()["title"] as? Channel {
self.channel.append(name)
}
you can't cast a String to a Custom data type (Channel) , so according to your data you can try this
if let title = document.data()["title"] as? String , let subtitle = document.data()["subtitle"] as? String {
let res = Channel(title:title,subtitle:subtitle)
self.channel.append(res)
}
My didSelectItemAt method is not being called and nothing is being printed into the console. I have user interaction turned on and I still can not get it to print out anything. I am not sure if my custom PinterestStyle Layout is causing this or if I am missing something. The ultimate goal would be to segue into a detail view controller showing the profile page of the cell selected. I will do that using prepareForSegue however I still can't even get it to print out the name of the cell when tapped.
class PagesCollectionViewController: UICollectionViewController, firebaseHelperDelegate {
var storageRef: StorageReference!{
return Storage.storage().reference()
}
var usersList = [String]()
var authService : FirebaseHelper!
var userArray : [Users] = []
var images: [UIImage] = []
var names: [String] = []
override func viewWillAppear(_ animated: Bool) {
if Global.Location != "" && Global.Location != nil
{
usersList = Global.usersListSent
print(usersList)
self.authService.ListOfUserByLocation(locationName: Global.Location, type: .ListByLocation)
}
}
override func viewDidLoad() {
self.collectionView?.allowsSelection = true
self.collectionView?.isUserInteractionEnabled = true
super.viewDidLoad()
self.authService = FirebaseHelper(viewController: self)
self.authService.delegate = self
setupCollectionViewInsets()
setupLayout()
}
private func setupCollectionViewInsets() {
collectionView!.backgroundColor = .white
collectionView!.contentInset = UIEdgeInsets(
top: 20,
left: 5,
bottom: 49,
right: 5
)
}
private func setupLayout() {
let layout: PinterestLayout = {
if let layout = collectionViewLayout as? PinterestLayout {
return layout
}
let layout = PinterestLayout()
collectionView?.collectionViewLayout = layout
return layout
}()
layout.delegate = self
layout.cellPadding = 5
layout.numberOfColumns = 2
}
func firebaseCallCompleted(data: AnyObject?, isSuccess: Bool, error: Error?, type: FirebaseCallType) {
if(type == .ListByLocation) {
if(isSuccess) {
self.userArray.removeAll()
self.images.removeAll()
self.images.removeAll()
if(data != nil) {
let dataDict = data as! NSDictionary
let keyArray = dataDict.allKeys
for i in 0 ..< keyArray.count {
var dict = NSDictionary()
dict = dataDict.object(forKey: keyArray[i]) as! NSDictionary
self.userArray.append(Users.init(data: dict))
}
}
self.collectionView?.reloadData()
}
else {
print(error?.localizedDescription)
SVProgressHUD.dismiss()
}
}
}
}
extension PagesCollectionViewController {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return userArray.count
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(userArray[indexPath.row].name)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "PagesCollectionViewCell",
for: indexPath) as! PagesCollectionViewCell
cell.nameLabel.text = userArray[indexPath.row].name
if let imageOld = URL(string: userArray[indexPath.row].photoURL){
cell.photo.sd_setImage(
with: imageOld,
placeholderImage: nil,
options: [.continueInBackground, .progressiveDownload]
)
}
return cell
}
}
extension PagesCollectionViewController : PinterestLayoutDelegate {
func collectionView(collectionView: UICollectionView,
heightForImageAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
var image: UIImage?
let url = URL(string: userArray[indexPath.row].photoURL)
let data = try? Data(contentsOf: url!)
image = UIImage(data: data!)
return (image?.height(forWidth: withWidth))!
}
func collectionView(collectionView: UICollectionView,
heightForAnnotationAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
return 30
}
}
Check 2 conditions:-
Make sure you have set delegate to UICollectionView
Make sure Content in PageCollectionCell like image having no user interaction enabled. If image user interaction is enabled then didSelectItemAt will not call.
as Manish Mahajan said a quick fix would be:
in cellForItemAt func set contentView as not clickable
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.contentView.isUserInteractionEnabled = false
return cell
}
I use viewController and collectionView inside collectionView. 1st collectionView need to do make a table, 2nd collectionView need to scrolling images.
I can display one image, but in my class Model should load more images (image, image2, image3).
So my class Model:
class Model {
var image: String
var image2: String
var image3: String
var images: [String] = []
var images2: [String] = []
var images3: [String] = []
var ref: FIRDatabaseReference!
init(snapshot: FIRDataSnapshot) {
ref = snapshot.ref
let value = snapshot.value as! NSDictionary
let snap = value["hall1"] as? NSDictionary
let snap2 = value["hall2"] as? NSDictionary
let snap3 = value["hall3"] as? NSDictionary
image = snap?["qwerty"] as? String ?? ""
image2 = snap2?["qwerty"] as? String ?? ""
image3 = snap3?["qwerty"] as? String ?? ""
}
}
My viewController:
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
var image: [Model] = []
var ref: FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference(withPath: "Студии2")
ref.observe(.value, with: { (snapshot) in
var newImage: [Model] = []
for imageSnap in snapshot.children {
let imageObj = Model(snapshot: imageSnap as! FIRDataSnapshot)
newImage.append(imageObj)
}
self.image = newImage
self.collectionView.reloadData()
})
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return image.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell11
cell.vc1 = self
cell.imagess = [image[indexPath.item]]
return cell
}
}
My collectionCell1:
class CollectionViewCell11: UICollectionViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
var imagess: [Model] = []
#IBOutlet weak var collectionView: UICollectionView!
var vc1: ViewController?
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imagess.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell12
cell.imageView.sd_setImage(with: URL(string: imagess[indexPath.item].image))
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if vc1 != nil {
let vc2 = vc1!.storyboard?.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
vc2.photo = [imagess[indexPath.item]]
let backItem = UIBarButtonItem()
backItem.title = ""
vc1!.navigationItem.backBarButtonItem = backItem
vc1!.navigationController?.pushViewController(vc2, animated: true)
}
}
}
And my collectionCell2:
class CollectionViewCell12: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
}
Use string in collectionCell1 impossible, because class Model have an array images for future pass in next viewController.
I'm novice, but i think need use another class Model inside new class. But I could be wrong.
I understand my mistake, my mistake is in collectionCell1 line -
cell.imageView.sd_setImage(with: URL(string: imagess[indexPath.item].image))
But i don't understand what I can do...
it should look like this:
But earlier i use type String in var imagess and now me need use class Model
GitHub Link
I put UICollectionView inside table cell in Home page. Each category row has their contents image. But, my problem is that when I run the app, the images under second row/second category do not show whenever I run the app.
The images will show after I click more button and it will goes to details UI. Then, I click on Back button and reach the Home page. At this time, the images shows properly. I tried many codes but I don't know where it is wrong.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.reloadData()
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
}
override func viewDidAppear(_ animated: Bool) {
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//getSearchWords()
DispatchQueue.main.async {
self.tableView.reloadData()
}
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
}
override func viewDidLoad() {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// debugPrint("HOme Table View Cell")
let cell = tableView.dequeueReusableCell(withIdentifier: "homecell") as! HomeCategoryRowCell
let list = sections[(indexPath as NSIndexPath).row]
cell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
cell.collectionViewOffset = self.storedOffsets[indexPath.row] ?? 0
DispatchQueue.main.async {
cell.categoryTitle.text = list.package_name
cell.mainAssociatedURL.text = list.package_url
}
cell.categoryTitle.font = UIFont.boldSystemFont(ofSize: 17.0)
cell.collectionView.tag = indexPath.row
cell.parentVC = self
cell.collectionView.reloadData()
return cell
}
extension Home : UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sections[collectionView.tag].packageTable.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCell", for: indexPath) as! HomeVideoCell
let list = sections[collectionView.tag].packageTable[indexPath.row]
if(list.poster_image_url != StringResource().posterURL){
let url = NSURL(string: list.poster_image_url)
do{
if let url = url {
_ = try Data(contentsOf:url as URL)
poster_url = list.poster_image_url
}
}catch let error {
//debugPrint("ERRor ::\(error)")
poster_url = StringResource().posterURL
}
}else{
poster_url = StringResource().posterURL
}
AsyncImageLoader.sharedLoader.imageForUrl(urlString: poster_url) { (image, url) -> () in
DispatchQueue.main.async(){
cell.movieTitle.text = list.name
if(url == StringResource().posterURL){
cell.imageView.image = UIImage(named: "sample_cover")
}else{
cell.imageView.image = image
}
}
}
// cell.layer.shouldRasterize = true
// cell.layer.rasterizationScale = UIScreen.main.scale
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Home Collection view at row \(collectionView.tag) selected index path \(indexPath)")
let list = sections[collectionView.tag].packageTable[indexPath.row]
self.prefs.set(list.poster_image_url, forKey: "poster_image_url")
self.prefs.set(list.name, forKey: "name")
self.prefs.set(list.assets_id, forKey: "VEDIO_ID")
debugPrint(list.name)
debugPrint(list.assets_id)
}
}
These above codes are written in Home.swift.
In HomeCell.swift,
class HomeCell : UITableViewCell {
var parentVC: UIViewController?
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var categoryTitle: UILabel!
#IBOutlet weak var SeeAll: UIButton!
#IBOutlet weak var mainAssociatedURL: UILabel!
let prefs:UserDefaults = UserDefaults.standard
var catId: String! = ""
var associated_url: String!
override func awakeFromNib() {
super.awakeFromNib()
SeeAll.setTitle(NSLocalizedString("See All >", comment: ""), for: .normal)
SeeAll.titleLabel!.font = UIFont (name: "Tharlon", size: 14)
}
#IBAction func btnSeeAll(_ sender: Any) {
catId = categoryTitle.text!
associated_url = mainAssociatedURL.text!
self.prefs.setValue(1, forKey: "PROVIDER_ID")
self.prefs.set(catId, forKey: "PROVIDER_NAME")
self.prefs.set(associated_url, forKey: "associated_url")
}
override func layoutSubviews() {
super.layoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
}
}
extension HomeCategoryRowCell {
func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forRow row: Int) {
self.collectionView.delegate = dataSourceDelegate
self.collectionView.dataSource = dataSourceDelegate
self.collectionView.tag = row
self.collectionView.setContentOffset(self.collectionView.contentOffset, animated:false) // Stops collection view if it was scrolling.
self.collectionView.reloadData()
}
var collectionViewOffset: CGFloat {
get { return collectionView.contentOffset.x }
set { collectionView.contentOffset.x = newValue }
}
}
Can anyone help me please?
Edit controller like below, you are calling too many reloadData.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.tableView.layoutIfNeeded()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//getSearchWords()
DispatchQueue.main.async {
self.tableView.reloadData()
}
self.tableView.layoutIfNeeded()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
and add:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView .deselectRow(at: indexPath, animated: false)
}
how to make this become looks like iPhone gallery (if the data meets the end, i want to add the label which is shows the month) and make it repeatable
it become looks like this
i've tried every objects and insert to uicollectionview, but it doesn't appear at all..is there any way to help?
here is my code
import UIKit
let reuseIdentifier = "Cell"
class SummaryViewController: UICollectionViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet var collectionview: UICollectionView!
var photos:NSArray?
var items = NSMutableArray()
var TableData:Array< String > = Array < String >()
var json:String = ""
var arrayOfMenu: [ImageList] = [ImageList]()
override func viewDidLoad() {
super.viewDidLoad()
self.setUpMenu()
collectionview.dataSource = self
collectionview.delegate = self
NSLog("%d", items.count)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayOfMenu.count //hitung banyak data pada array
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! UICollectionViewCell
let image = UIImage(named: items.objectAtIndex(indexPath.row) as! String)
let imageView = cell.viewWithTag(100) as! UIImageView
imageView.image = image
return cell
}
func setUpMenu() //membaca json pada setiap arraynya
{
var json: JSON = JSON (data: NSData())
DataManager.getactivityDataFromFileWithSuccess{ (data) -> Void in
json = JSON(data: data)
let results = json["results"]
for (index: String, subJson: JSON) in results {
}
for (var i = 0; i < json["Activity"].count; i++) {
if let icon: AnyObject = json["Activity"][i]["icon"].string {
self.items.addObject(icon)
dispatch_async(dispatch_get_main_queue(), {self.collectionView!.reloadData()})
var menu = ImageList(image: icon as! String)
self.arrayOfMenu.append(menu)
self.TableData.append(icon as! String)
}
}
}
}
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView
{
let header = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "headersection", forIndexPath: indexPath) as! UICollectionReusableView
return header
}
}