How to add XYPieChart and PNBarChart in same prototype cell - ios

My requirement is simple. I want to load a pie chart in first index of a UITableView. When I swipe right, pie chart (XYPieChart) should be replaced with a bar chart (PNBarChart). I have separately loaded pie chart and bar chart in separate rows. How can I achieve that swipe effect in UITableview.?
A UIView has to be added into the prototype cell and custom class of UIView has to set to XYPieChart and PNBarChart in the Identity Inspector in Xcode.
What I expect is an effect like page controller, but page controller loads two view controllers. Right?
Can I add a Page Control inside a prototype cell and add two UIViews into the page control?
Will the views change when I swipe the row?
I'm attaching some screenshots with this for clarity.
Pie Chart Screenshot
Pie Chart and Bar Chart in two rows

Try like this But code is in Swift
First Create Tableviewcontroller from Storyboard
Create CustomCell of TableView
import UIKit
class CustomCell: UITableViewCell {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControll: UIPageControl!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Now In Tableviewcontroller and add delegate UIScrollViewDelegate,UITableViewDelegate,UITableViewDataSource
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:CustomCell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! CustomCell;
let scrollViewWidth:CGFloat = tableView.frame.width
let scrollViewHeight:CGFloat = cell.contentView.frame.height
let imgOne = UIImageView(frame: CGRect(x:0, y:0,width:scrollViewWidth, height:scrollViewHeight))
imgOne.image = UIImage(named: "image1")
imgOne.tag = 1
let imgTwo = UIImageView(frame: CGRect(x:scrollViewWidth, y:0,width:scrollViewWidth, height:scrollViewHeight))
imgTwo.image = UIImage(named: "image2")
imgTwo.tag = 1
cell.scrollView.addSubview(imgOne)
cell.scrollView.addSubview(imgTwo)
cell.scrollView.contentSize = CGSize(width:scrollViewWidth * 2, height:cell.scrollView.frame.height)
cell.scrollView.delegate = self
cell.pageControll.currentPage = 0
pageCtrl = cell.pageControll
return cell;
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView){
if (scrollView.superview?.superview as? CustomCell) != nil{
let cell = scrollView.superview?.superview as! CustomCell
let pageWidth:CGFloat = self.view.frame.width
let currentPage:CGFloat = floor((scrollView.contentOffset.x - pageWidth/2)/pageWidth)+1
cell.pageControll.currentPage = Int(currentPage)
}
}

Related

Animate selected row on button tap

I currently have a table with a custom cell that contains a button and a label. Everything is working fine, the label displays the product name and the button responds to a button tap.
What I would like to be able to do is animate the width of the row/cell where the button was tapped.
Here is the code I currently have...
Main ViewController:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell", for: indexPath) as! CustomCell
let data = items[indexPath.row]
cell.displayProductName.text = data.productName
cell.buttonAction = { sender in
print("Button tapped...")
}
return cell
}
}
Custom Cell
class CustomCell: UITableViewCell {
var buttonAction: ((UIButton) -> Void)?
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func activeInactive(_ sender: UIButton) {
self.buttonAction?(sender)
}
}
Table View
How can I animate the width of the row where the button was tapped?
You could subclass UIButton and add an index property where you can store the represented index for the button:
import UIKit
class UIButtonWithIndex: UIButton {
var representedIndex: Int?
}
Then you should use this class in the storyboard instead UIButton.
After that add the button as an outlet in your cell and connect in in Interface Builder.
class CustomCell: UITableViewCell {
#IBOutlet weak var button: UIButtonWithIndex!
var buttonAction: ((UIButtonWithIndex) -> Void)?
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func activeInactive(_ sender: UIButtonWithIndex) {
self.buttonAction?(sender)
}
// Reset the represented index when reusing the cell
override func prepareForReuse() {
//Reset content view width
button.representedIndex = nil
}
}
Then when you dequeue the cell in cellForRowAt indexPath set the representedIndex property. Now in your buttonAction you should have the index of the row in which the button was tapped.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell", for: indexPath) as! CustomCell
let data = items[indexPath.row]
cell.displayProductName.text = data.productName
cell.button.representedIndex = indexPath.item
cell.buttonAction = { sender in
print("Button tapped... \(sender.representedIndex)")
}
return cell
}
Once you have the index you can retrieve the cell with cellForRowAtIndexPath
I'd recommend to:
Create subview, let's call it "resizeableContentView"
Add "resizeableContentView" as a child to cell's "contentView"
Add your views to "resizeableContentView" as a child
Set .clearColor for cell and "contentView" background color if needed
Set width for "resizeableContentView" by action
Don't forget reset cell when it's reused
you can try this boilerplate code:
cell.buttonAction = { sender in
UIView.animate(withDuration: 0.5) {
cell.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
}
}

In the detail title bar, show “Picture X of Y”

I'm having issues on 1 out of 3 challenges.
1st challenge
Use Interface Builder to select the text label inside your table view cell and adjust its font size to something larger – experiment and see what looks good. (Done)
2nd challenge
In your main table view, show the image names in sorted order (Done)
The 3rd challenge is
"Rather than show image names in the detail title bar, show “Picture X of Y”, where Y is the total number of images and X is the selected picture’s position in the array. Make sure you count from 1 rather than 0."
Im creating a simple table view row with cells, with images and when you click on it it will show you the picture.
I have an idea but I'm struggling. So I know that the title will use string interpolation and I have already created
var selectedPictureNumber = 0
var totalPictures = 0
Now I have tried creating
var selectedPictureNumber = pictures[indexPath.row]
or
var selectedPictureNumber = indexPath.row
But I get error messages
Heres what I got on mainViewController.swift
class ViewController: UITableViewController {
var pictures = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Adds a title to the navegation bar.
title = "Storm Viewer"
//This makes the title larger as well.
navigationController?.navigationBar.prefersLargeTitles = true
// THis code manages the images from the files saved locally to xcode
let fm = FileManager.default
let path = Bundle.main.resourcePath!
let items = try! fm.contentsOfDirectory(atPath: path)
for item in items {
if item.hasPrefix("nssl"){
pictures.append(item)
}
}
// This pictures.sort makes the arrays sort in order.
pictures.sort()
print(pictures)
}
// This code creates the table view rows , adds the table cells and adds the pictures to each table view cell.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pictures.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "picture", for: indexPath)
cell.textLabel?.text = pictures[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController{
vc.selectedImage = pictures[indexPath.row]
navigationController?.pushViewController(vc, animated: true)
}
}
}
Heres what I have on my detailViewController.swift
class DetailViewController: UIViewController {
// Outlets
#IBOutlet var imageView: UIImageView!
// Vars
var selectedImage : String?
var selectedPictureNumber = 0
var totalPictures = 0
override func viewDidLoad(){
title = selectedImage
super.viewDidLoad()
// THis code makes the title of the navegation bar on the detailViewcontroller the name of the img.
title = selectedImage
// This line will make it so that the title on the navegation bar will not be large.
navigationItem.largeTitleDisplayMode = .never
//This code lets the images that are on the detailViewController
if let imageToLoad = selectedImage{
imageView.image = UIImage(named: imageToLoad)
}
}
// This code will make the Navigation bar appear & Dissapear with a touch on the screen.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.hidesBarsOnTap = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.hidesBarsOnTap = false
}
}
I have got your question, just update your code in mainViewController.swift as follows:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController{
vc.selectedImage = pictures[indexPath.row]
vc.selectedPictureNumber = indexPath.row + 1
vc.totalPictures = pictures.count
navigationController?.pushViewController(vc, animated: true)
}
}
You have to add property of selectedPictureNumber and totalPictures to DetailViewController
var selectedPictureNumber : Int?
var totalPictures : Int?
Then in detailViewController.swift file, Inside viewDidLoad method add following code.
self.title = "Picture \(selectedPictureNumber!) of \(totalPictures!)"
To avoid warnings you need to add "!"
I hope this is solution which are you looking for.

When scroll table view content change

i have custom table view cell that having rating stars. i'm using https://github.com/hsousa/HCSStarRatingView for rating View.
there is my code for table view and cell view.
class RatingTableViewCell: UITableViewCell {
var value : CGFloat = 0.0
#IBOutlet weak var starRatingView: HCSStarRatingView!
#IBOutlet weak var titleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
initStarRatingView()
starRatingView.addTarget(self, action: #selector(DidChangeValue(_:)), for: .valueChanged)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
private func initStarRatingView() {
var scalingTransform : CGAffineTransform!
scalingTransform = CGAffineTransform(scaleX: -1, y: 1);
starRatingView.transform = scalingTransform
starRatingView.emptyStarImage = #imageLiteral(resourceName: "strokStar")
starRatingView.halfStarImage = #imageLiteral(resourceName: "halfStar")
starRatingView.filledStarImage = #imageLiteral(resourceName: "fillStar")
starRatingView.allowsHalfStars = true
}
#IBAction func DidChangeValue(_ sender: HCSStarRatingView) {
self.value = sender.value
}
class RatingViewController: CustomViewController,UITableViewDelegate,UITableViewDataSource {
var values : [CGFloat] = [0.5,0.0,0.0,0.0,0.0,0.0,0.0]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RatingTableViewCell", for: indexPath) as! RatingTableViewCell
values[indexPath.row] = cell.value
cell.starRatingView.value = values[indexPath.row]
return cell
}
//MARK: _Table data source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
values.count
}
}
there is a problem when i scroll table view. dequeue Reusable Cell data is wrong. how can update value data for each cell?
The problem is that you are storing the value in the cell. Take a look at those two lines:
let cell = tableView.dequeueReusableCell(withIdentifier: "RatingTableViewCell", for: indexPath) as! RatingTableViewCell
values[indexPath.row] = cell.value
You dequeue a cell and assign it's value to values[indexPath.row]. The problems that you are noticing when scrolling are caused by the fact that the reused cell was previously used for a different indexPath, which means that their value (that you assign to values[indexPath.row]) is meant for its previous indexPath.
To fix that, I would advise getting rid of the value variable in RatingTableViewCell. Instead, define a protocol RatingTableViewCellDelegate that will be used to inform the RatingViewController about the new value.

Make UITableViewCell actions overlap content

I was trying to go about making the actions for a UITableViewCell overlap the content of the cell, like in the National Geographic app:
table view
table view when swiped
I tried using a listener on the contentView of the cell to track the frame and keep it constant, but I was unable to get that to work (although it's possible it would, I'm kinda new to iOS).
If anyone has any suggestions for creating a similar effect, they would be much appreciated!
u can make your own custom cell and add a delete button and swipe gestures to make like button overlap the contents of the cell for example, try it out yourself, first create a sample project with single view app, and proceed
subclass the tabview cell with xib option selected and name it something like CustomCell , and in CustomCell.swift class past below code
import UIKit
class CustomCell: UITableViewCell {
var deleteButton:UIButton!
//create a custom cell if not in the reuse pool
class func customCell() -> CustomCell?
{
let aVar:Array = NSBundle.mainBundle().loadNibNamed("CustomCell", owner: nil, options: nil)
if aVar.last!.isKindOfClass(UITableViewCell)
{
return aVar.last as? CustomCell
}
return nil
}
//handle your left swipe
func swipeLeft()
{
print("swipe Left")
self.contentView.bringSubviewToFront(deleteButton!)
var frameRect:CGRect! = deleteButton!.frame
frameRect.origin.x = self.contentView.bounds.size.width - self.deleteButton!.frame.size.width
UIView.animateWithDuration(0.5) { () -> Void in
self.deleteButton!.frame = frameRect
}
}
//aslo the right swipe
func swipeRight()
{
print("swipe right")
var rect:CGRect! = deleteButton?.frame
rect.origin.x = self.contentView.bounds.size.width
UIView.animateWithDuration(0.5) { () -> Void in
self.deleteButton!.frame = rect
}
}
//hear we are adding the delete button in code, if u want add it in xib or (if u are using storyboard )
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
deleteButton = UIButton.init(type: .Custom)
deleteButton.setTitle("Delete", forState: .Normal)
deleteButton.setTitleColor(UIColor.whiteColor(), forState: .Normal)
deleteButton.backgroundColor = UIColor.redColor()
self.contentView.addSubview(deleteButton)
let gestureLeft:UISwipeGestureRecognizer = UISwipeGestureRecognizer.init(target: self, action: "swipeLeft")
gestureLeft.direction = .Left
self .addGestureRecognizer(gestureLeft)
let gestureRight:UISwipeGestureRecognizer = UISwipeGestureRecognizer.init(target: self, action: "swipeRight")
gestureRight.direction = .Right
self .addGestureRecognizer(gestureRight)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
//set the initial frame of delete button
override func layoutSubviews() {
super.layoutSubviews()
var rect:CGRect! = deleteButton?.frame
rect.size = CGSizeMake(100, 100)
rect.origin.x = self.contentView.bounds.size.width
deleteButton?.frame = rect
}
}
and make sure the tableview cell height to be of 100pt and in view controller set up a tableview in storyboard with datasource and delegate and implement the required delegate and datasource methods
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:CustomCell? = tableView.dequeueReusableCellWithIdentifier("CELL") as? CustomCell
if cell == nil
{
cell = CustomCell .customCell()
}
return cell as CustomCell!
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}

When I swipe a UITableViewCell, the header view moves as well

So, I have a few Swipe actions like delete, block, etc in my UITableView. I wanted to add headers to separate my two sections. So, I added a prototype cell, named it HeaderCell and then went to the view. I added one label, named headerLabe. My problem is that when I swipe for the actions, the header cells were moving as well, which looked bad. I researched, and found a solution to just return the contentView of the cell. However, when I do this, the label has not shown up. I have tried a dozen different solutions, and nothing has worked, so I have turned to SO. Can anyone help me?
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell : CustomHeaderTableViewCell = tableView.dequeueReusableCellWithIdentifier("HeaderCell") as! CustomHeaderTableViewCell
if section == 0 {
headerCell.headerLabel.text = "Thank You's"
} else if section == 1 {
headerCell.headerLabel.text = "Conversations"
}
return headerCell.contentView
}
Thanks so much.
You can use a section Header as #ozgur suggest.If you still want to use a cell.
Refer to this datasource method
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
if indexPath = YourHeaderCellIndexPath{
return false
}
return true
}
check the following methods
In your UIViewController use the following
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("HeaderCell") as! WishListHeaderCell
headerCell.lblTitle.text = cartsData.stores_Brand_Name
let imgVw = UIImageView()
imgVw.frame = CGRectMake(8, 18, 25, 25)
imgVw.image = UIImage(named: "location.png")
let title = UILabel()
title.frame = CGRectMake(41, 10, headerCell.viwContent.frame.width - 49, 41)
title.text = cartsData.stores_Brand_Name
title.textColor = UIColor.whiteColor()
headerCell.viwContent.addSubview(imgVw)
headerCell.viwContent.addSubview(title)
return headerCell.viwContent
}
In your UITableViewCell use the following
import UIKit
class HeaderCell: UITableViewCell {
#IBOutlet weak var viwContent: UIView!
#IBOutlet weak var imgIcn: UIImageView!
#IBOutlet weak var lblTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.viwContent.backgroundColor = UIColor.grayColor()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//UITableViewCell
let headerCell = tableView.dequeueReusableCellWithIdentifier("headerCell") as! SecJobCCHeaderTableViewCell
// Cell Rect
var cellRect : CGRect = headerCell.frame
cellRect.size.width = screenBounds.width
// Header Footer View
let headerFooterView = UITableViewHeaderFooterView(frame : cellRect)
//Adding Gesture
let swipeGestRight = UISwipeGestureRecognizer(target: self, action:#selector(AddSecJobCostCentreViewController.draggedViewRight(_:)))
swipeGestRight.enabled = true
swipeGestRight.direction = UISwipeGestureRecognizerDirection.Right
headerFooterView.addGestureRecognizer(swipeGestRight)
// Update Cell Rect
headerCell.frame = cellRect
// Add Cell As Subview
headerCell.tag = 1000
headerFooterView.addSubview(headerCell)
// Return Header Footer View
return headerFooterView
}
func draggedViewRight(sender:UISwipeGestureRecognizer) {
// Swipe Gesture Action
let currentHeaderView = sender.view?.viewWithTag(1000) as! SecJobCCHeaderTableViewCell
}

Resources