iOS Scrolling Parallax Effect in Table View Cell Swift 4 - ios

I create Scrolling Parallax Effect in Table View Cell in my project but i have some freeze when i scrolling.
My code in custom Table View Cell :
class newsTableViewCell: UITableViewCell {
#IBOutlet weak var imageNews: UIImageView!
#IBOutlet weak var titleLable: UILabel!
#IBOutlet weak var imageHeight: NSLayoutConstraint!
#IBOutlet weak var imageTop: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
imageNews.clipsToBounds = true
}
}
Code in my Main View Controller:
var paralaxOfSetSpeed: CGFloat = 70
var cellHeignt: CGFloat = 170
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! newsTableViewCell
let good = data[indexPath.row]
cell.titleLable.text = (good["title"] as! String)
cell.imageHeight.constant = paralaxImageHeight
cell.imageTop.constant = paralaxOfSet(newOfSetY: tableView.contentOffset.y, cell: cell)
let newsImage = good["image"] as! PFFile
newsImage.getDataInBackground{ (imageData, error) in
if imageData != nil {
let image = UIImage(data: imageData!)
cell.imageNews.image = image
} else {
print(error ?? "getDataInBackground error")
}
}
return cell
}
func paralaxOfSet(newOfSetY: CGFloat, cell: UITableViewCell) -> CGFloat {
return (newOfSetY - cell.frame.origin.y) / paralaxImageHeight * paralaxOfSetSpeed
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let ofSetY = tableView.contentOffset.y
for cell in tableView.visibleCells as! [newsTableViewCell] {
cell.imageTop.constant = paralaxOfSet(newOfSetY: tableView.contentOffset.y, cell: cell)
}
}
var paralaxImageHeight: CGFloat {
let maxOffSet = (sqrt(pow(cellHeignt, 2) + 4 * paralaxOfSetSpeed * self.tableView.frame.height ) - cellHeignt) / 1.5
return maxOffSet + self.cellHeignt
}
This is my code, main problem it's scrolling because it's a not smooth.
UPDATE:
So in the end, i want to get something like this:

I would suggest you to look into the below libraries:
They have implemented the parallax view in TableViewCell
https://github.com/marty-suzuki/SAParallaxViewControllerSwift
Hope it helps.

Related

Adding button to Tableview cell

Okay, so I am trying to add a button to a table view cell. My table view cell has an image, label and a button. I set the button to be hidden in storyboard. Now I'm trying to show the button if the label text is truncated, I set the number of lines on my label to 3 in storyboard. It works but when I scroll up and down, somehow some cells with the label text not truncated shows a button. Why?
This is how my cell file looks atm.
class ItemCell: UITableViewCell {
#IBOutlet weak var itemImageView: UIImageView!
#IBOutlet weak var itemTitleLabel: UILabel!
#IBOutlet weak var itemDetailLabel: UILabel!
#IBOutlet weak var moreButton: UIButton!
var item: Item? {
didSet {
configureCell()
}
}
private func configureCell() {
guard let item = item else { return }
itemImageView.image = item.image
itemTitleLabel.text = item.name
itemDetailLabel.text = item.description
if itemDetailLabel.isTruncated {
moreButton.isHidden = false
} else {
moreButton.isHidden = true
}
}
extension UILabel {
var isTruncated: Bool {
guard let labelText = text else {
return false
}
let labelTextSize = (labelText as NSString).boundingRect(
with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: [.font: font],
context: nil).size
return labelTextSize.height > bounds.size.height
}
}
This is how my cellForRowAt looks like.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as? ItemCell else {
return UITableViewCell()
}
let item = categories[indexPath.section].items[indexPath.row]
cell.item = item
print("Name: \(item.name)\nSection: \(indexPath.section)\nRow: \(indexPath.row)\nisTruncated: \(cell.itemDetailLabel.isTruncated)\n")
return cell
}

Can't call object from another class

I have a table view with expanding cells. The expanding cells come from a xib file. In the class of the table is where all of the code is that controls the expansion and pulling data from plist. I'm trying to add a close button but only want it to show when the cell is expanded. As it stands, I can't reference the button to hide it because it's in another class. Here is how I am trying to access it:
import UIKit
class SecondPolandViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var customTableViewCell:CustomTableViewCell? = nil
var items = [[String:String]]()
override func viewDidLoad() {
super.viewDidLoad()
**REFERENCING CLASS**
customTableViewCell = CustomTableViewCell()
let nib = UINib.init(nibName: "CustomTableViewCell", bundle: nil)
self.tableView.register(nib, forCellReuseIdentifier: "cell")
self.items = loadPlist()
}
func loadPlist()->[[String:String]]{
let path = Bundle.main.path(forResource: "PolandResourceList", ofType: "plist")
return NSArray.init(contentsOf: URL.init(fileURLWithPath: path!)) as! [[String:String]]
}
var selectedIndex:IndexPath?
var isExpanded = false
func didExpandCell(){
self.isExpanded = !isExpanded
self.tableView.reloadRows(at: [selectedIndex!], with: .automatic)
}
}
extension SecondPolandViewController:UITableViewDataSource, UITableViewDelegate{
***HIDING BUTTON***
let button = customTableViewCell?.closeButton
button?.isHidden = true
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedIndex = indexPath
self.didExpandCell()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
cell.selectionStyle = .none
let item = self.items[indexPath.row]
cell.titleLabel.text = item["title"]
cell.shortLabel.text = item["short"]
cell.otherImage.image = UIImage.init(named: item["image"]!)
cell.thumbImage.image = UIImage.init(named: item["image"]!)
cell.longLabel.text = item["long"]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let height = UIScreen.main.bounds.height
if isExpanded && self.selectedIndex == indexPath{
//return self.view.frame.size.height * 0.6
return 400
}
return 110
//return height * 0.2
}
}
This does not hide it though.
Here is the xib that I am calling from if it helps. It is probably simple, I am just a newly self taught developer.
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var closeButton: UIImageView!
#IBOutlet weak var otherImage: UIImageView!
#IBOutlet weak var thumbImage: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var shortLabel: UILabel!
//#IBOutlet weak var longLabel: UITextView!
#IBOutlet weak var longLabel: UITextView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
//let width = UIScreen.main.bounds.width
//let height = UIScreen.main.bounds.height
//thumbImage.frame.size.width = height * 0.19
//thumbImage.frame.size.height = height * 0.19
}
}
It seems like that you just need to add these lines into cellForRowAt:indexPath method:
if indexPath == selectedIndexPath {
cell.closeButton.isHidden = false
} else {
cell.closeButton.isHidden = true
}
You may add them right before return line
The normal iOS answer for this is a delegate, but you could get away with a simple closure in this case.
In CustomTableViewCell, add
public var closeTapped: ((CustomTableViewCell) -> ())?
Then in that class, when close is tapped, call
self.closeTapped?(self)
In the VC, in cellForRowAt,
cell.closeTapped = { cell in
// do what you want with the VC
}
For delegates, this might help: https://medium.com/#jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef
The quick answer to why to prefer delegates over the closure is that its a handy way to group a bunch of these together. It's what UITableViewDelegate is (which you are using). Also, it's a common iOS idiom.
I wrote about this here: https://app-o-mat.com/post/how-to-pass-data-back-to-presenter for a similar situation (VC to VC communication)

Updating Constraints in UITableViewCell are not properly

I want to make a dynamic cell in my UITableView in case of image height. Everything works properly, but when I scroll my UITableView, debugger is yelling me about bad constraints in my cell, but cells look exactly what I want. I can't be calm when I see this debug info ;)
My CustomCell class looks like that:
class CustomCell: UITableViewCell {
//MARK: - Outlets
#IBOutlet weak var photoImageView: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
//MARK: - Variables
var dream: MyDream? {
didSet {
if let item = dream, let image = UIImage(data: item.photoData) {
scaleImageView(image: image)
photoImageView.image = image
titleLabel.text = item.title
}
}
}
func scaleImageView(image: UIImage) {
let imageWidth = image.size.width
let imageScale = image.size.height / image.size.width
if imageWidth > photoImageView.bounds.width {
heightConstraint.constant = photoImageView.bounds.size.width * imageScale
} else {
heightConstraint.constant = imageWidth * imageScale
}
}
}
my cell in .xib looks like that:
TitleLabel doesn't have a height constraint. Leading, Top, Trailing, Bottom only.
and my ViewController is quite simple and looks like that:
class ViewController: UIViewController {
//MARK: - Outlets
#IBOutlet var tableView: UITableView!
//MARK: - Variables
lazy var dreams = [MyDream]()
override func viewDidLoad() {
super.viewDidLoad()
createModel() // I mock data here :)
setTableView()
}
func setTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
tableView.register(UINib(nibName: String(describing: CustomCell.self), bundle: nil), forCellReuseIdentifier: "cell")
}
func createModel() {
for i in 0..<12 {
if i < 6 {
if let image = UIImage(named: "\(i + 1)"), let imageData = UIImageJPEGRepresentation(image, 100) {
let dream = MyDream(photoData: imageData, title: "Image number \(i + 1)")
dreams.append(dream)
}
} else {
if let image = UIImage(named: "\(i - 5)"), let imageData = UIImageJPEGRepresentation(image, 100) {
let dream = MyDream(photoData: imageData, title: "Image number \(i + 1)")
dreams.append(dream)
}
}
}
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dreams.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCell
cell.dream = dreams[indexPath.row]
cell.selectionStyle = .none
return cell
}
}
How it looks like in simulator:
every thing look fine, but console gives me this kind of error:
I don't know why? I tried to use layoutIfNeeded(), updateConstraints() and setNeedsLayout() in didSet in CustomCell but is doesn't help. Any suggestions?
I guess you're having some redundant constraints on your image.
They could be height / bottom / top constraints. One should be removed

Modify custom TableViewCell size based on contents

I want to be able to resize my custom TableViewCell to wrap its contents, which can be a variably-sized image and/or any number of lines of text, followed by a vote bar.
My OMCFeedTableViewCell looks like this:
I have tried setting up contraints, modifying frames, nothing works correctly.
This is my code so far:
In FeedViewController:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("OMCFeedCell", forIndexPath: indexPath) as! OMCFeedTableViewCell
let p = posts[indexPath.row]
cell.selectionStyle = .None
let data = p.content!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let content = try! JSON(NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers))
let image = content["image"].string
let uploads = uploadAPI()
uploads.tryGetImage(image) {
(result: UIImage?) in
if let i = result {
cell.postImage.image = i
cell.postImage.contentMode = .ScaleAspectFill
let containerWidth = cell.postImage.frame.width
let aspectRatio = i.size.height / i.size.width
let scaledHeight = aspectRatio * containerWidth
// do something with the calculated height
} else {
// kill the imageview
}
}
if let text = content["text"].string {
cell.postText.text = text
// calculate the textview height
} else {
// kill the text section
}
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if heights[indexPath.row] == nil {
// calculate the correct overall height
}
return heights[indexPath.row]!
}
In OMCFeedTableViewCell:
import UIKit
class OMCFeedTableViewCell: UITableViewCell {
#IBOutlet weak var postImage: UIImageView!
#IBOutlet weak var postText: UILabel!
#IBOutlet weak var voteLabel: UILabel!
#IBOutlet weak var downVoteButton: UIButton!
#IBOutlet weak var upVoteButton: UIButton!
}
You can use UITableViewAutomaticDimension and it would automatically adjust your cell size depending on contents.
self.tableView.rowHeight = UITableViewAutomaticDimension
also set your estimatedRowHeigh
self.tableView.estimatedRowHeight = 120
Also from experience, sometime the bottom constraint on main content view has given me a problem, you can remove it if it bothered you.

How to make dynamic height of UITableViewCell

I would like to create dynamic height of UITableViewCell depending tablecell content.
But i couldn't.
import UIKit
class WorkItemCell: UITableViewCell{
#IBOutlet weak var item_view: UIView!
#IBOutlet weak var minimum_startday_label: CustomLabel!
#IBOutlet weak var catchcopyLabel: CustomLabel!
#IBOutlet weak var stationLabel: UILabel!
#IBOutlet weak var paymentLabel: UILabel!
#IBOutlet weak var limitLabel: UILabel!
#IBOutlet weak var mainjobLabel: UILabel!
#IBOutlet weak var companyLabel: UILabel!
#IBOutlet weak var workstartdateLabel2: UILabel!
#IBOutlet weak var fav_view: ImageWithView!
#IBOutlet weak var img_seen_view: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
//self.fav_view = ImageWithView.instance()
self.fav_view.checkedImage = UIImage(named: "fav_on")
self.fav_view.uncheckedImage = UIImage(named: "fav_off")
}
override func layoutSubviews() {
super.layoutSubviews()
self.contentView.layoutIfNeeded()
self.catchcopyLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.catchcopyLabel.bounds)
}
private var _workdic: NSDictionary?
var workdic: NSDictionary? {
get {
return _workdic
}
set(workdic) {
_workdic = workdic
if let workdic = workdic {
let workplace = workdic["WorkPlace"] as? String
let companyname = workdic["CompanyName"] as? String
let jobname = workdic["JobName"] as? String
let payment = workdic["Payment"] as? String
let workstartdate = workdic["WorkStartDate"] as? String
let workdatetime = workdic["WorkDateTime"] as? String
let minimumday = workdic["MinimumWorkDay"] as? String
let applyenddate = workdic["ApplyEndDate"] as? String
let catchcopy = workdic["CatchCopy"] as? String
if notnullCheck(catchcopy){
//行間
let attributedText = NSMutableAttributedString(string: catchcopy!)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 5
paragraphStyle.lineBreakMode = NSLineBreakMode.ByTruncatingTail
attributedText.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSMakeRange(0, attributedText.length))
self.catchcopyLabel.attributedText = attributedText
}
self.catchcopyLabel.sizeToFit()
if let payment_constant = payment{
self.paymentLabel.text = payment_constant
}
if notnullCheck(minimumday) && notnullCheck(workstartdate){
self.minimum_startday_label.text = minimumday!+" "+workstartdate!
}else{
self.minimum_startday_label.text = ""
}
if let applyenddate_constant = applyenddate{
self.limitLabel.text = applyenddate_constant
}
if let jobname_constant = jobname{
self.mainjobLabel.text = jobname_constant
}
if let workdatetime_constant = workdatetime{
self.workstartdateLabel2.text = workdatetime_constant
}
if let companyname_constant = companyname{
self.companyLabel.text = companyname_constant
}
self.stationLabel.text = workplace
self.item_view.sizeToFit()
}
}
}
class func heightForRow(tableView: UITableView, workdic: NSDictionary?) -> CGFloat {
struct Sizing {
static var cell: WorkItemCell?
}
if Sizing.cell == nil {
Sizing.cell = tableView.dequeueReusableCellWithIdentifier("WorkItemCell") as? WorkItemCell
}
if let cell = Sizing.cell {
cell.frame.size.width = CGRectGetWidth(tableView.bounds)
cell.workdic = workdic
cell.setNeedsDisplay()
cell.layoutIfNeeded()
let size = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
print(size)
return size.height+1
}
return 0
}
}
Above code has item_view.
It has all label and image.
It is setted margin at 4 points(top,left,bottom,right),5px.
I use above cell for data list.
It has around 5000 counts.
Catch copy label is often setted 2 line sentence.
I want to change item_view height and cell height each catch_copy_label's height.
But I couldn't.
I always have gotten same height,26px.
(366.5, 26.0)
(366.5, 26.0)
What should i do?
I have add the part of view controller's source.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat{
if self.int_total == 0{
return self.view.frame.size.height
}else{
if let workdic: AnyObject = workdata.safeObjectAtIndex(indexPath.row){
return WorkItemCell.heightForRow(self.workview, workdic: (workdic as! NSDictionary),base_height:170)
}else{
return 199
}
}
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 199
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.int_total == 0{
return 1
}
return self.workdata.count
}
/*
Cellに値を設定する.
*/
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Cellの.を取得する.
if self.int_total > 0{
let cell = workItemCell(tableView, cellForRowAtIndexPath: indexPath, str_xib: "WorkItemCell")
return cell
}else{
let nocell: NoCountCell = tableView.dequeueReusableCellWithIdentifier("NoCountCell", forIndexPath: indexPath) as! NoCountCell
nocell.conditionButton.addTarget(self, action: "onClickBack:", forControlEvents: .TouchUpInside)
//初期が終わったらfalse
if self.init_loading{
nocell.conditionButton.hidden = true
nocell.messageLabel.hidden = true
}else{
nocell.conditionButton.hidden = true
nocell.messageLabel.hidden = false
}
return nocell
}
}
func workItemCell(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath,str_xib:String) ->WorkItemCell{
let wcell: WorkItemCell = tableView.dequeueReusableCellWithIdentifier(str_xib, forIndexPath: indexPath) as! WorkItemCell
wcell.separatorInset = UIEdgeInsetsZero
wcell.selectionStyle = UITableViewCellSelectionStyle.None
updateCell(wcell, atIndexPath: indexPath)
return wcell
}
func updateCell(cell:UITableViewCell,atIndexPath:NSIndexPath){
}
func showWorkItem(wcell:WorkItemCell,workdic:NSDictionary){
wcell.workdic = workdic
}
I have posted capture.
This is in Objective C.Calculate the height of cell's content in heightForRowAtIndexPath method add it in one array as
- (CGFloat)tableView:(UITableView *)t heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
text =[NSString stringWithFormat:#"%#",[content_array objectAtIndex:indexPath.row]];
CGSize textSize = [text sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(self.tblView.frame.size.width - PADDING * 3, 1000.0f)];
NSLog(#"%f",textSize.height);
[height_Array addObject:[NSString stringWithFormat:#"%f", textSize.height]];
return textSize.height;
}
Now in cellForRowAtIndexPath method set the frame of title using height array as:-
CGRect newFrame = txtTitle.frame;
newFrame.size.height = [[height_Array objectAtIndex:indexPath.row] floatValue];
txtTitle.frame = newFrame;
I finaly got answer.
But i have calculated height manually.
I calculated the label's height at each time.
This label has multi line.
Then I set height other height + the height which was calcuted.
class func heightForCatchCopy(tableView: UITableView, workdic: NSDictionary?) -> CGFloat {
struct Sizing {
static var cell: WorkItemCell?
}
if Sizing.cell == nil {
Sizing.cell = tableView.dequeueReusableCellWithIdentifier("WorkItemCell") as? WorkItemCell
}
if let cell = Sizing.cell {
cell.frame.size.width = CGRectGetWidth(tableView.bounds)
cell.workdic = workdic
let size = cell.catchcopyLabel.intrinsicContentSize()
return size.height
}
return 0
}

Resources