I am wondering what is the best approach to add an Activity Indicator to the bottom of the UICollectionView.
I would like to able to show and display this cell, and I am wondering how should I do this.
I have searched SO and I have found people suggesting adding a new cell to the last row. But I would like to have the instance of the activity indicator so that I can turn it on or off.
Any suggestions?
You can add a new cell to the last row of your collection view and create a custom UICollectionViewCell class for that cell where you can enter the logic of your cell e.g.:
import UIKit
class LoadingCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func awakeFromNib() {
self.activityIndicator.startAnimating()
}
func stopLoading() {
self.activityIndicator.stopAnimating()
}
}
You can then call the function stopLoading() in your ViewController when you want the activity indicator to stop.
Related
I'm developing an app for my school that helps students better understand their grades by getting an analysis on their portfolio of assignments for each class. Right now I am at the point of letting the user create the classes they are in and customize the information within it that is displayed in a tableview with custom cells. The user gives the majority of the information in a child view where they input information such as class name, teacher, grade weighting, etc. I wanted to give the user the ability to change the color of the cell once they are viewing the TableView with all the cells - classes - they made. I decided to go about this by having the cells have a UIButton that they can click on for their determined cell to then pull up a UIColorPickerViewController.
What I wanted to happen was...
User taps button in cell
UIPickerViewController is pulled up
User selects their desired color
User exits UIPickerViewController
Cell is changed to the color
What is actually happening is this
User taps button in cell
Cell background becomes black right as UIPickerViewController is presented
User selects their desired color
User exits UIPickerViewController
Cell remains black
I used a delegate to send the information from the cells and then I used the "colorPickerViewControllerDidFinish()" function and it's still not working out. When I did some debugging I found that the value of the UIColorPickerViewController is actually being stored in the variable I am using, but only after I have already assigned it's value to the cell background so I'm unsure what to do. As you can probably tell, I'm new to swift so apologies for any stupid mistakes in my code.
Custom Cell File
// Protocol for VC's to conform to so they can distinguish which cell has a button being tapped
protocol newlyCreatedCellDelegate: AnyObject
{
func didTapButton(title: String, cellView: UIView)
}
class newlyCreatedClass: UITableViewCell {
// Telling the delegate what to do once they are assigned
weak var delegate: newlyCreatedCellDelegate?
#IBOutlet weak var classContentView: UIView!
#IBOutlet weak var classUIView: UIView!
#IBOutlet weak var classNameLabel: UILabel!
#IBOutlet weak var classTeacherNameLabel: UILabel!
#IBOutlet weak var pointType1NameLabel: UILabel!
#IBOutlet weak var pointType2NameLabel: UILabel!
#IBOutlet weak var pointType3NameLabel: UILabel!
#IBOutlet weak var percent1Label: UILabel!
#IBOutlet weak var percent2Label: UILabel!
#IBOutlet weak var percent3Label: UILabel!
#IBOutlet weak var colorButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
colorButton.layer.cornerRadius = 21
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
// Essentially creating the prep list for the delegate. If they are called - whoever it is - they will go through this 'checklist'
#IBAction func colorButtonTapped(_ sender: Any)
{
delegate?.didTapButton(title: classNameLabel.text!, cellView: classUIView)
}
}
ViewController Extensions
extension ClassSelection: newlyCreatedCellDelegate
{
func didTapButton(title: String, cellView: UIView)
{
let colorPickerVC = UIColorPickerViewController()
colorPickerVC.delegate = self
present(colorPickerVC, animated: true, completion: nil)
colorPickerViewControllerDidFinish(colorPickerVC)
// 'cellBackgroundColor' is a variable declared in the VC to transfer the UIColor value
cellView.backgroundColor = cellBackgroundColor
}
}
extension ClassSelection: UIColorPickerViewControllerDelegate
{
func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
cellBackgroundColor = viewController.selectedColor
}
}
You should implement one more UIColorPickerViewControllerDelegate method:
func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) {
cellBackgroundColor = viewController.selectedColor
}
It's a great start! As a really direct answer to your original question:
The reason for your problem is that in your didTapButton function you are presenting the color picker, but then immediately telling the app that the user is done with the picker, and then immediately setting the background color to cellBackgroundColor, which I assume has a default value of UIColor.black.
Instead you should delete those last 2 lines in your didTapButton function - just initialize the picker, set the delegate, and present the picker. Then the delegate method you chose - colorPickerViewControllerDidFinish isn't really the correct one for your purpose. Instead consider using the didSelect delegate method (see docs). When that method is called it will pass you the color the user selected, which you can simply use to set your background color and refresh your tableView cell if needed.
Since you mention you are a new Swift dev I will also mention that UITableView reuses its cells, so simply setting the background color of a cell once will not have the result you are expecting. You will see that as you scroll the cells up and down the colors will change in the various cells, so ultimately you'll need to store the color selections in another way so that each time a cell is being dequeued you can set the correct color based on user input. That part is outside of the scope of the original question, but just letting you know.
I have a UITableView which displays some data. If there is no data to display, I show an empty with a button in it. I'm taking advantage of the tableView's backgroundView feature to display my empty state. The problem is that I cannot interact with that button. I assume the tableView disables interaction with the background view. Is there some way to enable it?
You should create strong variable to your background view controller.
class ExampleController: UIViewController {
...
#IBOutlet weak var tableView: UITableView!
private let emptyStateVC = EmptyStateVc(nibName: "EmptyStateVc", bundle: nil)
...
private func setupTableView() {
tableView.backgroundView = emptyStateVC.view
tableView.backgroundView?.isUserInteractionEnabled = true
}
}
I am trying to change colour of the view inside the uicollectionviewcell. But I am not able to change the colour. When I try to connect to the UIViewController to our view controller it say " cannot connect the repetitive content as an outlet.".
When I change the background of the cell it comes like this
As to make it round I am using view and the giving it layer radius properties.
What I am trying to achieve is:
The values are coming from the model class that I have created and assigned it to UIcolectionviewcell. Model contains only one text field that shows the tags.
When user select any tags the background and text colour will change.I am not to achieve this. It might be easy to do but somehow I am not able to achieve this.
Try to change the background color of your rounded element and not of the entire cell
You can create custom UICollectionViewCell and use it to access different items inside of it, like the textfield with your tag
I've added the sample code to achieve your requirements, Please refer and try implementing based on the following code:
//Your model class
class TagModel{
var tag:String!
var selected:Bool!
init(tag:String, selected:Bool) {
self.tag = tag
self.selected = selected
}
}
//your cell with Xib
class TagCell:UICollectionViewCell{
#IBOutlet weak var tagLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
func setTag(_ tagModel:TagModel){
tagLabel.layer.masksToBounds = true
tagLabel.layer.cornerRadius = tagLabel.frame.size.height/2
tagLabel.text = tagModel.tag
if tagModel.selected{
tagLabel.textColor = .white
tagLabel.backgroundColor = .blue
}else{
tagLabel.textColor = .gray
tagLabel.backgroundColor = .lightGray
}
}
}
//Your ViewController which has `UICollectionView`
class TagViewController:UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
var tagModels:[TagModel]!
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
tagModels[indexPath.item].selected = !tagModels[indexPath.item].selected
collectionView.reloadItems(at: [indexPath])
}
}
Note: Please take this code as sample and make modifications based on your implementations.
I would like to add Ads in my App, like Native Ads or Parallax.
The App is mainly an UICollectionView with many cells displaying different information.
I am using an SDK in order to load the Ad Object.
I have made a custom UICollectionViewCell class in order to init the cell and load the Native Ad as a cell in my UICollectionView.
#objc class AdsCollectionViewCell: BaseCollectionViewCell, AdapterAdsDelegate {
#IBOutlet var outletNativeTitle: UILabel!
#IBOutlet var outletImageNative: UIImageView!
#IBOutlet var outletButtonAction: UIButton!
#IBOutlet var clickableView: UIView!
#IBOutlet var mediaContainer: UIView!
#IBOutlet var bottomView: UIView!
var adFactory = AdsSDKFactory()
var adObject = AdsObject()
#objc func initCell(viewController: HomeViewController) {
DispatchQueue.main.async {
self.isHidden = true
}
// Set some properties for the Ads SDK
adFactory.nativeDelegate = self
adObject.viewController = viewController
adFactory.placementId = "154569"
// method from the SDK to request the Ad
adFactory.loadAds()
}
It is working but my main issue is that I am displaying an empty cell and the content is displayed when I got the delegate response method from the Ad SDK with the Ad Object.
Here is the delegate method I receive in my AdsCollectionViewCell.
func adObjectDidLoad(adView: AdsObject!) {
self.adObject = adView
self.updateCellOutlets() // my method to update labels from the adView
self.setNeedsDisplay()
self.delegate.tileCellDidSucceed!(self)
}
I would like to first init and make the call to the adFactory.loadAds in my HomeViewController, so that when I got the delegate response and the adObject, I don't updateCellOutlets directly but I can self.delegate.tileCellDidSucceed!(self) which is the delegate from my BaseCollectionViewCell in order to notify my HomeViewController that the cell is ready.
How can init a cell outside of the cellForRow method to dynamically insert it when the Ad is loaded ?
EDIT: I have followed the advices and create a Singleton AdsLoader class, that set all the parameters to request the ad, and also make the load request.
I set my HomeViewController as delegate so that I can get the view ad as response directly in my HomeViewController.
My issue now is to insertObject in the collectionView at the right indexPath. Which object should I insert?
I am unable to get a clear picture of the code and specific requirements of your AdSDK. So i will go ahead and write a pseudo code as per my understanding of what needs to be done. Here is what I purpose, instead of calling adFactory.loadAds() into the UICollectionViewCell object, you can perform this task into a different class object. Let say AdLoader
So your add loader should be something like this.
class Adloader {
var adFactory = AdsSDKFactory()
typealias adLoadCompletionHandler = (adView: AdsObject) -> Void
var loadCompletion
func initLoader() {
//Set some properties for the Ads SDK
adFactory.nativeDelegate = self
// method from the SDK to request the Ad
adFactory.loadAds()
}
}
Once the ad is loaded, you can use a completion handler to get update the collection view.
func adObjectDidLoad(adView: AdsObject!) {
adLoadCompletionHandler(adView)
}
So your code can be something like this.
if (needToInsertAd) {
var newAd = Adloader.initLoader()
newAd.adLoadCompletionHandler {
//Code to insert cell at indexpath
}
}
Hope this helps you.
I'd like to use RxSwift in my project, but because I'm newbie I maybe misunderstand some principles. Its clear for me how to catch button presses or use rxswift with UITableView with dynamic cells (there are a lot of tutorials for that), but I don't understand how to use it with UITableView with STATIC cells - I'd like to develop something like iOS Settings.app. Could you show me example? Is it a good practice to use rxswift for it? Or maybe I should use something else?
You can drag a #IBOutlet weak var button: UIButton! from static table view cell button, So it's something like this:
class TableViewController: UITableViewController {
#IBOutlet weak var button: UIButton!
...
override func viewDidLoad() {
super.viewDidLoad()
...
button.rx.tap
.subscribe()
.disposed(by: disposeBag)
}
...
}
Hope this may help.
To handle static cells, you have to use a UITableViewController (which I assume you know) but you can still use the rx operators on its tableView.
Of course you don't need to use the items operator on it because the cells are already built, but you can still use itemSelected to determine which cell was tapped on:
final class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.rx.itemSelected
.subscribe(onNext: { print("selected index path \($0)") })
.disposed(by: bag)
}
let bag = DisposeBag()
}