how can I display the following scenes hierarchy in swift using collectionView? - ios

I have the following display scenes available. I am getting confused what type of hierarchy of controls I should take to display these type of view in xib .
please give ideas to show these types of scenes. because my items are coming dynamically . Its not fixed. so if I took tableview to display the first items and its categories then where should i display the rest items.
Edited
I took four sections. In 1st section collection and delivery buttons. In 3rd notes and in 4th allergy & checkout .
In 2nd my order items are there. but here I have two level of data.. order item name like chicken kabab small,... etc and 2nd level its addons like plain nan, bottle of drink,... etc. Here my order items is iterating in cell as well as my addons are iterating. I took the order items name in cell. now where should i take the addon items programatically and how to set the size of each cell based on its all contents inside it.
class cartVC: UIViewController ,UITableViewDataSource,UITableViewDelegate,UITextViewDelegate{
var tableData = ["al","dbd","gdge","kjdkas","al","dbd","gdge","kjdkas","al","dbd","gdge","kjdkas","al","dbd","gdge","kjdkas"]
var mainview = UIView()
#IBOutlet weak var cartTableView: UITableView!
#IBAction func backBtn(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func changeColor(sender:UISegmentedControl){
switch(sender.selectedSegmentIndex){
case 0:
print("collection clicked")
case 1:
print("delivery clicked")
default:
self.view.backgroundColor = UIColor.blueColor()
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 4
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var rowcount = 0
if section == 0{
rowcount = 0
}
if section == 1 {
rowcount = tableData.count
}
if section == 2{
rowcount == 0
}
if section == 3{
rowcount == 0
}
return rowcount
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0{
let headerView = UIView()
//set the frame
let frame = UIScreen.mainScreen().bounds
// headerView.frame = CGRectMake(0, 0, tableView.frame.size.width, 60)
headerView.frame = CGRectMake(frame.minX , frame.minY, frame.width, 60)
headerView.backgroundColor = UIColor.whiteColor()
//Initialize segment control
let items = ["Collection","Delivery"]
let customSC = UISegmentedControl(items: items)
customSC.selectedSegmentIndex = 0
//set the frame amd segmented control
customSC.frame = CGRectMake(frame.minX + 10, frame.minY + 5, frame.width - 20, 30)
// style the segmented control
customSC.layer.cornerRadius = 5.0
customSC.backgroundColor = UIColor.clearColor()
customSC.tintColor = UIColor.redColor()
//add target action method
customSC.addTarget(self, action: #selector(CartViewController.changeColor(_:)), forControlEvents: .ValueChanged)
//add subview
headerView.addSubview(customSC)
//Add label
let headinglbl = UILabel(frame: CGRect(x: frame.minX + 10, y: frame.minY + 40, width: tableView.frame.size.width, height: 20))
headinglbl.text = "Your Order"
headinglbl.font = UIFont.boldSystemFontOfSize(17)
headinglbl.textColor = UIColor.blackColor()
headinglbl.textAlignment = .Center
headerView.addSubview(headinglbl)
mainview = headerView
}
if section == 2{
let totalView = UIView()
totalView.frame = CGRectMake(0, 0, tableView.frame.size.width, 60)
totalView.backgroundColor = UIColor.clearColor()
//Add discount label
let discount = 14.5
let discountlbl = UILabel(frame: CGRectMake(10, 0, tableView.frame.size.width, 20))
discountlbl.text = "Online Collection Discount(\(discount)%)"
discountlbl.font = UIFont.systemFontOfSize(14)
discountlbl.textColor = UIColor.darkGrayColor()
discountlbl.textAlignment = .Left
totalView.addSubview(discountlbl)
//Add discount price
let discountprice = UILabel(frame: CGRectMake(tableView.frame.size.width-60, 0, tableView.frame.size.width, 20))
discountprice.text = "£ 1.27"
discountprice.font = UIFont.systemFontOfSize(14)
discountprice.textColor = UIColor.blackColor()
discountprice.textAlignment = .Left
totalView.addSubview(discountprice)
//Add label
let lbl = UILabel(frame: CGRectMake(10, 20, tableView.frame.size.width, 40))
lbl.text = "Total"
lbl.font = UIFont.boldSystemFontOfSize(20)
lbl.textColor = UIColor.blackColor()
lbl.textAlignment = .Left
totalView.addSubview(lbl)
//calculate amount label
let totalAmountLbl = UILabel(frame: CGRectMake(totalView.frame.width-70, 20, totalView.frame.width, 40))
totalAmountLbl.text = "£ 0.0"
totalAmountLbl.font = UIFont.boldSystemFontOfSize(20)
totalAmountLbl.textColor = UIColor.blackColor()
totalAmountLbl.textAlignment = .Left
totalView.addSubview(totalAmountLbl)
mainview = totalView
}
if section == 3{
let footerView = UIView()
footerView.frame = CGRectMake(0, 0, tableView.frame.size.width, 200)
footerView.backgroundColor = UIColor.clearColor()
//Add note label
let notelbl = UILabel(frame: CGRectMake(10, 10, tableView.frame.size.width, 20))
notelbl.text = "Leave a note"
notelbl.font = UIFont.boldSystemFontOfSize(17)
notelbl.textColor = UIColor.blackColor()
notelbl.textAlignment = .Left
footerView.addSubview(notelbl)
//Add a note textview
let noteTxt = UITextView()
noteTxt.frame = CGRectMake(10, 40, footerView.frame.width-20, 50)
noteTxt.backgroundColor = UIColor.lightGrayColor()
noteTxt.keyboardType = UIKeyboardType.Default
noteTxt.text = "e.g. Instructions about yout order"
noteTxt.textColor = UIColor.blackColor()
noteTxt.delegate = self
footerView.addSubview(noteTxt)
// Add allergy button
let allergyBtn = UIButton(type:.System)
allergyBtn.frame = CGRectMake(50, 100, 200, 20)
allergyBtn.setTitle("Do You have any allergy ?", forState: .Normal)
allergyBtn.setTitleColor(UIColor.redColor(), forState: .Normal)
allergyBtn.titleLabel?.font = UIFont(name: "", size: 10)
footerView.addSubview(allergyBtn)
// Add checkout button
let checkoutBtn = UIButton(type:.System)
checkoutBtn.frame = CGRectMake(100, 140, 100, 40)
checkoutBtn.setTitle("Check out", forState: .Normal)
checkoutBtn.setTitleColor(UIColor.whiteColor(), forState: .Normal)
checkoutBtn.titleLabel?.font = UIFont(name: "", size: 10)
checkoutBtn.backgroundColor = UIColor.redColor()
checkoutBtn.layer.cornerRadius = 5
footerView.addSubview(checkoutBtn)
mainview = footerView
}
return mainview
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cartcell")! as! CartTableViewCell
cell.itemLabel.text = tableData[indexPath.row]
return cell
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
var heightCount:CGFloat = 0
if section == 0{
heightCount = 60.0
}
if section == 2{
heightCount = 60.0
}
if section == 3{
heightCount = 200.0
}
return heightCount
}
My customcell code
import UIKit
class CartTableViewCell: UITableViewCell {
let padding: CGFloat = 5
var background: UIView!
var itemLabel: UILabel!
var priceLabel: UILabel!
var deleteBtn:UIButton!
override func awakeFromNib() {
super.awakeFromNib()
backgroundColor = UIColor.clearColor()
selectionStyle = .None
background = UIView(frame: CGRectZero)
background.alpha = 0.6
contentView.addSubview(background)
deleteBtn = UIButton(frame: CGRectZero)
deleteBtn.setImage(UIImage(named: "deleteBin.png"), forState: .Normal)
contentView.addSubview(deleteBtn)
itemLabel = UILabel(frame: CGRectZero)
itemLabel.textAlignment = .Left
itemLabel.textColor = UIColor.whiteColor()
contentView.addSubview(itemLabel)
priceLabel = UILabel(frame: CGRectZero)
priceLabel.textAlignment = .Center
priceLabel.textColor = UIColor.whiteColor()
contentView.addSubview(priceLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
background.frame = CGRectMake(0, padding, frame.width, frame.height-2 * padding)
deleteBtn.frame = CGRectMake(padding, (frame.height - 25)/2, 40, 25)
priceLabel.frame = CGRectMake(frame.width-100, padding, 100, frame.height - 2 * padding)
itemLabel.frame = CGRectMake(CGRectGetMaxX(deleteBtn.frame) + 10, 0, frame.width - priceLabel.frame.width - CGRectGetMaxX(deleteBtn.frame) + 10, frame.height)
}
}

As our mates already said about using tableview and sections, Here we gonna follow the same way.Since it is a broad topic to explain i'll give some hint and at last you can find link for demo project.
First add a tableview in your storyboard then add collection,Delivery & Your order objects as tableview header
Create a new class subclass of UITableviewcell with xib let's name it as Cell1.Now add delete icon, main dish label and price label,for sub items we gonna use another UITableview.
Now create another UITableviewcell with xib name it as Cell2, prepare that xib for sub items and their price.
In cell1 numberOfSectionsInTableView return number of main dish count and in numberOfRowsInSection return 1, Now load name of all main dishes in their respective label's
Upto now we having some number of section(depending on number of main items) each section having one UITableview.
Now we have to change height of tableview cell dynamically depending on SubItems count. so in heightForRowAtIndexPath i have added following lines
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let subItems = tableContainer![indexPath.section].valueForKey("additional_items") as! NSArray
var defaultCellHeight:CGFloat = 37//Consider 37 as height of cell without subitems
//Following for loop keeps on increasing defaultCellHeight depending on available count
for _ in subItems {
defaultCellHeight += 37
}
return defaultCellHeight + 20
}
Since it is hard to explain everything deeply i have provide code for heightForRowAtIndexPath.While looking into the demo project you'll understand everything
NOTE : Upto now we have loaded all main dishes details, and we have provided enough room for upcoming sub item's.
In Cell1 class add tableview delegate and datasource in awakeFromNib,add all datasource methods as required.set numberOfSectionsInTableView as 1 and numberOfRowsInSection as subitem count
That's it we have loaded tableview dynamically as per your requirement.
Now at last add discount, total, leave a note& Checkout objects in separate tableviewcell class an load it at last index.
Or add add all those objects inside a UIView and add it as Main tableview's footer.
NOTE : The above hints are just for reference, For better clarification of concept i have added a demo project's github repo.
RESULT :

Related

How to change the Title label text alignment of the section when it was present

I'm loading the UITableView with two section and I've used viewForHeaderInSection method to set the title for each section. Initially I've set the title label text alignment as .left. Now when I scroll the table view and the second section was present i want to change the title label text alignment to .center. And when I scroll down the table view i want to show as initial(like second section text alignment as .left). How can I Do that?
Here is the TableView Delegatemethod I've used to load the section.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerLabel:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 20))
headerLabel.textColor = UIColor.white
headerLabel.textAlignment = .left
headerLabel.font = Fonts.kFontWith_14
if section == 0
{
headerLabel.text = NSLocalizedString("Suggested", comment: "")
headerLabel.backgroundColor = Colors.kAppBgColor
headerLabel.textAlignment = .left
}
else{
headerLabel.text = NSLocalizedString("Contacts", comment: "")
headerLabel.backgroundColor = Colors.kLightBlueColor
headerLabel.textAlignment = .left
}
headerLabel.sizeToFit()
return headerLabel
}
Take one bool variable on global, The follwoing delegate will respond you when scroll happens
var isDirection = Bool()
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if velocity.y > 0 {
print("up")
isDirection = true
}
if velocity.y < 0 {
print("down")
isDirection = false
}
}
Then write a condition in respective `tableview` delegate
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerLabel:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 20))
headerLabel.textColor = UIColor.white
headerLabel.textAlignment = .left
headerLabel.font = Fonts.kFontWith_14
if section == 0
{
headerLabel.text = NSLocalizedString("Suggested", comment: "")
headerLabel.backgroundColor = Colors.kAppBgColor
}
else{
headerLabel.text = NSLocalizedString("Contacts", comment: "")
headerLabel.backgroundColor = Colors.kLightBlueColor
}
if isDirection{
headerLabel.textAlignment = .left
}else{
headerLabel.textAlignment = .center
}
return headerLabel
}
Remove
headerLabel.sizeToFit()
This is sample, you enable bool variable based on your scroll direction. If delegate does not call, reload your table on scrollViewWillEndDragging
Here is the way for my problem
Take a bool value and a CGPoint globally and assign the UITableView.ContentOffset value to the defaultOffset in viewDidload Method.
var isScroll:Bool = Bool()
var defaultOffSet = CGPoint()
override func viewDidLoad() {
super.viewDidLoad()
defaultOffSet = tableView.contentOffset
}
And I've used this scrollView delegate method
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if contactsTableView.rectForHeader(inSection: 1).origin.y > contactsTableView.contentOffset.y-defaultOffSet.y {
isScrolled = false
}
else if tableView.rectForHeader(inSection: 0).origin.y <= tableView.contentOffset.y-defaultOffSet.y{
isScrolled = true
}
tableView.reloadData()
}
And In my tableview viewForHeaderInSection
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerLabel:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 20))
headerLabel.textColor = UIColor.white
headerLabel.textAlignment = .left
headerLabel.font = Fonts.kFontWith_14
if section == 0
{
headerLabel.text = NSLocalizedString("Suggested", comment: "")
headerLabel.backgroundColor = Colors.kAppBgColor
headerLabel.textAlignment = .left
}
else{
headerLabel.text = NSLocalizedString("Contacts", comment: "")
headerLabel.backgroundColor = Colors.kLightBlueColor
if isScrolled{
headerLabel.textAlignment = .center
headerLabel.backgroundColor = Colors.kAppBgColor
}
}
}
}

Swift - Customize view to show over an empty table cells

I am using Swift 3, Xcode 8.2.
I've been able to create a label to cover the empty table view cells when there are none to display.
My code is below and it is located in the subclass of UITableViewController.
override func numberOfSections(in tableView: UITableView) -> Int {
// if there are scans to display...
if items.count > 0 {
tableView.backgroundView = nil
tableView.separatorStyle = .singleLine
return 1
}
else { // otherwise, return 0, remove cell lines, and display a Label
let rect = CGRect(x: 0,
y: 0,
width: tableView.bounds.size.width,
height: tableView.bounds.size.height)
let noScanLabel: UILabel = UILabel(frame: rect)
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.textAlignment = NSTextAlignment.center
tableView.backgroundView = noScanLabel
tableView.separatorStyle = .none
return 0
}
}
Here is the result.
Looks fine. But how, do I make it such that I include another line of text with a downward arrow pointing at the raised center button. Something like "Click here to start a scan"?
I've tried adding new line characters to the noScanLabel.text field but that didn't work out. Any pointers in the right direction would be helpful.
The simple solution is to set numberOfLines to 0 on noScanLabel. This way, the new lines will show.
let noScanLabel: UILabel = UILabel(frame: rect)
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.numberOfLines = 0
noScanLabel.textAlignment = NSTextAlignment.center
Note than in such cases, I would recommend, for better maintainability, to actually remove the TableView from the UIViewController (hence not inherit from UITableViewController) and replace it with an empty view when you detect no scans are available. This will make each state more independent of each other and make maintenance easier.
There are a few ways achieve your goal. There is a well known library called DZNEmptyDataSet for handling empty tableviews and collectionviews . https://github.com/dzenbot/DZNEmptyDataSet
The other way would be to create a uiview with your specified rect and then add two labels to that uiview. One would be your noScanLabel and the other would be a label or image containing your arrow. You can set the layout constraints as required so that the arrow appears pointing down.
This code seems to work well. Change constraints if needed
let rect = CGRect(x: 0, y: 0, width: tableview.bounds.size.width, height: tableview.bounds.size.height)
let noDataView = UIView(frame: rect)
let noScanLabel = UILabel()
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.textAlignment = NSTextAlignment.center
let arrowLabel = UILabel()
arrowLabel.text = "Add Arrow Image to this label"
arrowLabel.textColor = UIColor.gray
arrowLabel.font = UIFont.boldSystemFont(ofSize: 24)
arrowLabel.textAlignment = NSTextAlignment.center
noScanLabel.widthAnchor.constraint(equalToConstant: 100)
arrowLabel.widthAnchor.constraint(equalToConstant: 50)
noDataView.addSubview(noScanLabel)
noDataView.addSubview(arrowLabel)
arrowLabel.translatesAutoresizingMaskIntoConstraints = false
noDataView.translatesAutoresizingMaskIntoConstraints = false
noScanLabel.translatesAutoresizingMaskIntoConstraints = false
self.tableview.addSubview(noDataView)
noDataView.isHidden = false
noDataView.centerXAnchor.constraint(equalTo: self.tableview.centerXAnchor).isActive = true
noDataView.centerYAnchor.constraint(equalTo: self.tableview.centerYAnchor).isActive = true
noScanLabel.centerXAnchor.constraint(equalTo: noDataView.centerXAnchor).isActive = true
noScanLabel.topAnchor.constraint(equalTo: noDataView.centerYAnchor).isActive = true
arrowLabel.centerXAnchor.constraint(equalTo: noDataView.centerXAnchor).isActive = true
arrowLabel.topAnchor.constraint(equalTo: noScanLabel.bottomAnchor).isActive = true
The other option is to set number of lines to zero as mentioned already
noScanLabel.numberLines = 0
You can take UIView and add your all UILabel and arrow Image on UIView and then assign that UIView to backgroundView of TableView.
Like this.
override func numberOfSections(in tableView: UITableView) -> Int {
// if there are scans to display...
if items.count > 0 {
tableView.backgroundView = nil
tableView.separatorStyle = .singleLine
return 1
}
else { // otherwise, return 0, remove cell lines, and display a Label
let rect = CGRect(x: 0,
y: 0,
width: tableView.bounds.size.width,
height: tableView.bounds.size.height)
let messageBaseView = UIView(frame: rect)
//Add your first label..
let noScanLabel: UILabel = UILabel()
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.textAlignment = NSTextAlignment.center
messageBaseView.addSubView(noScanLabel)
//Add your second label.. and your arrow image here on messageBaseView
//Assign messageBaseView to backgroundView of tableView
tableView.backgroundView = messageBaseView
tableView.separatorStyle = .none
return 0
}
}

How to make Horizontal scrollView and some buttons in it (programming)

sorry,I have a problem.I don't use storyBoard.
I want to make a view like this.
photo by terenceLuffy/AppStoreStyleHorizontalScrollView
but I try to do this.
Finlly,scrollView just show last button like this.
This is code:
var scView:UIScrollView = UIScrollView()
var buttonPadding:CGFloat = 10
var xOffset:CGFloat = 10
scView.backgroundColor = UIColor.blue
scView.translatesAutoresizingMaskIntoConstraints = false
func viewDidLoad() {
for i in 0 ... 10 {
let button = UIButton()
button.tag = i
button.backgroundColor = UIColor.darkGray
button.setTitle("\(i)", for: .normal)
button.addTarget(self, action: #selector(btnTouch), for: UIControlEvents.touchUpInside)
button.frame = CGRect(x: xOffset, y: CGFloat(buttonPadding), width: 70, height: 30)
xOffset = xOffset + CGFloat(buttonPadding) + button.frame.size.width
scView.addSubview(button)
}
scView.contentSize = CGSize(width: xOffset, height: scView.frame.height)
}
please help me.
Thanks all!
There are two ways of achieving it :) One the hard way using scrollView and calculating offset and setting views programmatically on ScrollView as subView else use CollectionView
Step 1:
Add a UICollectionView to storyboard, set the height of collectionView to match your requirement :)
Step 2
Create a cell, size of which depends on your requirement. I have created a cell with background colour orange/pink. Added a label on it to show your number.
Step 3:
Set the reusable cell identifier to the cell and set its class as well :)
Step 4 :
Set collectionView scroll direction to horizontal :)
Step 5:
Now implement collectionView delegates and data source methods :)
extension ViewController : UICollectionViewDataSource,UICollectionViewDelegate {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath) as! MyCollectionViewCell
cell.myLabel.text = "ABCD"
return cell;
}
}
Thats all :)
Final O/P
Look at collectionView with cell ABCD :D
Additional Info
If you are dragging a collectionView on UIViewController, you might see that the collectionView leaves a gap at the top which you can solve by unchecking Adjust ScrollView Insets of ViewController :)
I have tried your code and changed it a bit. It works as expected, unless you have something other going on:
var scView:UIScrollView!
let buttonPadding:CGFloat = 10
var xOffset:CGFloat = 10
override func viewDidLoad() {
super.viewDidLoad()
scView = UIScrollView(frame: CGRect(x: 0, y: 120, width: view.bounds.width, height: 50))
view.addSubview(scView)
scView.backgroundColor = UIColor.blue
scView.translatesAutoresizingMaskIntoConstraints = false
for i in 0 ... 10 {
let button = UIButton()
button.tag = i
button.backgroundColor = UIColor.darkGray
button.setTitle("\(i)", for: .normal)
//button.addTarget(self, action: #selector(btnTouch), for: UIControlEvents.touchUpInside)
button.frame = CGRect(x: xOffset, y: CGFloat(buttonPadding), width: 70, height: 30)
xOffset = xOffset + CGFloat(buttonPadding) + button.frame.size.width
scView.addSubview(button)
}
scView.contentSize = CGSize(width: xOffset, height: scView.frame.height)
}
import UIKit
class ViewController: UIViewController {
var imageStrings:[String] = []
var myScrollView:UIScrollView!
#IBOutlet weak var previewView: UIImageView!
var scrollWidth : CGFloat = 320
let scrollHeight : CGFloat = 100
let thumbNailWidth : CGFloat = 80
let thumbNailHeight : CGFloat = 80
let padding: CGFloat = 10
override func viewDidLoad() {
super.viewDidLoad()
imageStrings = ["image01","image02","image03","image04","image05","image06","image07","image08"]
scrollWidth = self.view.frame.width
//setup scrollView
myScrollView = UIScrollView(frame: CGRectMake(0, self.view.frame.height - scrollHeight, scrollWidth, scrollHeight))
//setup content size for scroll view
let contentSizeWidth:CGFloat = (thumbNailWidth + padding) * (CGFloat(imageStrings.count))
let contentSize = CGSize(width: contentSizeWidth ,height: thumbNailHeight)
myScrollView.contentSize = contentSize
myScrollView.autoresizingMask = UIViewAutoresizing.FlexibleWidth
for(index,value) in enumerate(imageStrings) {
var button:UIButton = UIButton.buttonWithType(.Custom) as! UIButton
//calculate x for uibutton
var xButton = CGFloat(padding * (CGFloat(index) + 1) + (CGFloat(index) * thumbNailWidth))
button.frame = CGRectMake(xButton,padding, thumbNailWidth, thumbNailHeight)
button.tag = index
let image = UIImage(named:value)
button.setBackgroundImage(image, forState: .Normal)
button.addTarget(self, action: Selector("changeImage:"), forControlEvents: .TouchUpInside)
myScrollView.addSubview(button)
}
previewView.image = UIImage(named: imageStrings[0])
self.view.addSubview(myScrollView)
}
// Do any additional setup after loading the view, typically from a nib.
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func changeImage(sender:UIButton){
let name = imageStrings[sender.tag]
previewView.image = UIImage(named: name)
}
}
Its easy with minimal use of storyboard. You can avoid that rather easily. All you need are:
Scroll view
Horizontal stack view
Set the constraints of the above as shown in the project
A number of buttons - as many view controllers that need to be displayed.
A container view - this will be the view that will hold the active view controller.
Please refer to this project for some sample code:
https://github.com/equitronix/horizontalScrollMenu
//make that variable gloabl
let button = UIButton.init(type: .custom)
var arrMmenu : NSMutableArray = []
var selectedIndex : Int = 0
#IBOutlet weak var btnScrollView: UIScrollView!
var lineArray : NSMutableArray = []
var buttonArray : NSMutableArray = []
//Call buttonCreated()
//MARK:- Create an Horizontral LayOut
func buttonCreated()
{
btnScrollView.contentSize.width = 0
namesOfMenu = ["GMC","GPA","GTL"]
var scrollwidth: CGFloat = 10.0
for j in 0..<namesOfMenu.count
{
let name = namesOfMenu[j] as! String
let size : CGSize? = name.size(withAttributes : [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 10.0)])
let textsize = CGSize(width : CGFloat(ceilf(Float(size!.width))), height : CGFloat(ceilf(Float(size!.height))))
var strikwidth : CGFloat = textsize.width
strikwidth = textsize.width + 30
let frame = CGRect(x: scrollwidth , y : CGFloat(7),width :CGFloat(strikwidth + 20) , height :CGFloat(20))
let frameLine = CGRect(x: scrollwidth , y : CGFloat(30),width :CGFloat(strikwidth + 20) , height :CGFloat(2))
let button = UIButton(type: .custom)
let line = UIView()
line.frame = frameLine
button.tag = j
view.tag = j
button.frame = frame
line .backgroundColor = UIColor.red
button.backgroundColor = UIColor.red
button.setTitleColor(UIColor.white, for: UIControlState.normal)
button.layer.borderColor = UIColor.white.cgColor
button.titleLabel?.textAlignment = .center
button.addTarget(self, action: #selector(self.buttonEvent(_:)), for: .touchUpInside)
button.setTitle(name, for : .normal)
scrollwidth = scrollwidth + strikwidth + 30
let strofMenu = namesOfMenu[selectedIndex] as! String
if (j == selectedIndex)
{
if(strofMenu == "GMC")
{
// apicall()
}
line.backgroundColor = hexStringToUIColor(hex: "#3174C7")
button.backgroundColor = UIColor.clear
button.setTitleColor(hexStringToUIColor(hex: "#3174C7"), for: UIControlState.normal)
}else
{
line.backgroundColor = UIColor.clear
button.backgroundColor = UIColor.clear
button.setTitleColor(MyConstant.Color.dark_gray_shade, for: UIControlState.normal)
}
button.titleLabel?.font = MyConstant.fontApp.Regular
buttonArray.add(button)
lineArray.add(line)
btnScrollView.addSubview(button)
btnScrollView.addSubview(line)
}
btnScrollView.contentSize = CGSize(width : CGFloat(scrollwidth), height : CGFloat(40.0))
btnScrollView.isPagingEnabled = false
btnScrollView.showsHorizontalScrollIndicator = false
btnScrollView.showsVerticalScrollIndicator = false
}
//MARK:- Button Event
#objc func buttonEvent(_ sender : UIButton)
{
let index = sender.tag
selectedIndex = index
let getRepoName = namesOfMenu[index] as! String
print(getRepoName)
for i in 0..<buttonArray.count
{
let buttonone : UIButton = (buttonArray[i] as! UIButton)
let line : UIView = (lineArray[i]) as! UIView
if i == selectedIndex
{
buttonone.backgroundColor = UIColor.clear
line.backgroundColor = hexStringToUIColor(hex: "#3174C7")
buttonone.setTitleColor(hexStringToUIColor(hex: "#3174C7"), for: .normal)
//
// if(getRepoName == "GMC")
// {
// clickedCellIndexes.add(0)
//
// arrmutablefordata.removeAllObjects()
// tblhome.reloadData()
// apicall()
// }
//buttonone.titleLabel
}else
{
buttonone.backgroundColor = UIColor.clear
line.backgroundColor = UIColor.clear
buttonone.setTitleColor(MyConstant.Color.dark_gray_shade, for: .normal)
}
}
}
Doing it from the storyboard using scroll view and stack views makes this a very simple solution. Along with a bunch of sub view controllers and transitions. I have posted sample code on GitHub. Please take a look here:
https://github.com/equitronix/horizontalScrollMenu
Let me know if this works.

Swift 3 UITableView cell seperator disappears when i remove cell subviews

I have an array type of string arrays and I print it to tableView within for loop.I know its a bad practice loop inside cellForRowAt indexPath: function but I don't have any solution.My Problem is every time I move my tableview on simulator,i insert more subviews on existing ones.it overwrites older ones and to prevent i use
for view in cell.subviews {
view.removeFromSuperview()
}
but it deletes my cell seperators with my older cell labels.How can i remove only cell data not my seperator.
func tableView(_ tableView: UITableView, cellForRowAt indexPath:IndexPath) -> UITableViewCell {
var cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
//print(cell.subviews)
for view in cell.subviews {
view.removeFromSuperview()
}
for i in 0..<globalHeaderStructArray.count{
var newLabel = UILabel(frame: CGRect(x:xCoordination, y:10, width:Double(globalHeaderStructArray[i].width)!, height:30.0))
newLabel.font = UIFont(name: "Avenir", size: 17.0)
newLabel.textColor = UIColor.darkGray
newLabel.text = "\(globalDataArray[indexPath.row][i])"
var scrollview = UIScrollView(frame: cell.bounds)
scrollview.contentSize = CGSize(width:cell.bounds.width * 5, height:cell.bounds.height) // will be 5 times as wide as the cell
scrollview.isPagingEnabled = true
cell.contentView.addSubview(scrollview)
cell.addSubview(newLabel)
xCoordination += Double(globalHeaderStructArray[i].width)!
}
xCoordination = 0.0
return cell
}
You can set tag to your label and scrollView object and check that inside loop like this.
for view in cell.subviews {
if view.tag == 101 || view tag == 102 {
view.removeFromSuperview()
}
}
for i in 0..<globalHeaderStructArray.count{
var newLabel = UILabel(frame: CGRect(x:xCoordination, y:10, width:Double(globalHeaderStructArray[i].width)!, height:30.0))
newLabel.font = UIFont(name: "Avenir", size: 17.0)
newLabel.textColor = UIColor.darkGray
newLabel.text = "\(globalDataArray[indexPath.row][i])"
newLabel.tag = 101
var scrollview = UIScrollView(frame: cell.bounds)
scrollview.contentSize = CGSize(width:cell.bounds.width * 5, height:cell.bounds.height) // will be 5 times as wide as the cell
scrollview.isPagingEnabled = true
scrollview.tag = 102
cell.contentView.addSubview(scrollview)
cell.addSubview(newLabel)
xCoordination += Double(globalHeaderStructArray[i].width)!
}
Tips: It is batter if you use interface builder to design your cell instead of adding UI element at run time.

Add subview on tableView cells causes a sad patchwork

I want to give a chat aspect to a table view of messages in my iPhone app.
In order to perform a quality render I add two subviews: the text and the "container" which is just a view with background color.
Even if it works the first time, when I scroll, it becomes really messy because it keeps adding lots of subviews.
Here you can see it when clean
And then when it has become messy
Here is the function to handle the transform, it's called when scrolling.
func configChatCell(cell: UITableViewCell, text: String, color:UIColor)
{
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
cell.textLabel?.textColor = UIColor.whiteColor()
let fixedWidth = cell.bounds.width - 150
let textView: UITextView = UITextView(frame: CGRect(x: 0, y: 0, width: 10, height: CGFloat.max))
textView.text = text
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max))
var newFrame = textView.frame
newFrame.size = CGSize(width: min(newSize.width, fixedWidth), height: newSize.height)
textView.sizeThatFits(newFrame.size)
textView.frame = newFrame
textView.backgroundColor = UIColor.clearColor()
self.rowHeight = textView.frame.height+20
let view = UIView()
view.backgroundColor = color
print(textView.frame.height+10)
view.frame = CGRect(x: 0, y: 5, width: textView.frame.width+50, height: textView.frame.height+10)
view.layer.cornerRadius = 5
cell.backgroundColor = UIColor.clearColor()
cell.contentView.addSubview(view)
cell.contentView.addSubview(textView)
cell.contentView.sendSubviewToBack(view)
}
If I remove the subviews each time I scroll, nothing appears on screen.
Can somebody help me to find a solution? Or is there any other way to do this?
Thanks in advance!
I quickly wrote up something for this.
It starts with the ChatCell
class ChatCell: UITableViewCell {
var messageLabel: UILabel? {
didSet {
messageLabel?.text = message
}
}
var message: String? {
didSet {
messageLabel?.text = message
}
}
class func messageCell(withText text: String, leading: Bool = true) -> ChatCell {
let cell = ChatCell()
cell.message = text
// Make the container
let container = UIView()
container.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(container)
container.topAnchor.constraintEqualToAnchor(cell.contentView.topAnchor, constant: 8).active = true
container.bottomAnchor.constraintEqualToAnchor(cell.contentView.bottomAnchor, constant: -8).active = true
if leading {
container.leadingAnchor.constraintEqualToAnchor(cell.contentView.leadingAnchor, constant: leading ? 8 : 8*8).active = true
container.trailingAnchor.constraintLessThanOrEqualToAnchor(cell.contentView.trailingAnchor, constant: leading ? -8*8 : -8).active = true
} else {
container.leadingAnchor.constraintGreaterThanOrEqualToAnchor(cell.contentView.leadingAnchor, constant: leading ? 8 : 8*8).active = true
container.trailingAnchor.constraintEqualToAnchor(cell.contentView.trailingAnchor, constant: leading ? -8*8 : -8).active = true
}
// Make the messageLabel.
let messageLabel = UILabel()
messageLabel.numberOfLines = 0
messageLabel.textColor = UIColor.whiteColor()
messageLabel.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(messageLabel)
// Add constraints.
messageLabel.topAnchor.constraintEqualToAnchor(container.topAnchor, constant: 8).active = true
messageLabel.bottomAnchor.constraintEqualToAnchor(container.bottomAnchor, constant: -8).active = true
messageLabel.leadingAnchor.constraintEqualToAnchor(container.leadingAnchor, constant: 8).active = true
messageLabel.trailingAnchor.constraintEqualToAnchor(container.trailingAnchor, constant: -8).active = true
cell.messageLabel = messageLabel
container.backgroundColor = UIColor(red:0.19, green:0.70, blue:1.00, alpha:1.00)
container.layer.cornerRadius = 12.0
return cell
}
}
The cell also includes support for leading and trailing messages, for back and forth conversation. Perhaps make an array of tuples like this:
let messages: [(message: String, leading: Bool)] = [("Hello", true), ("My name is John Doe and this works quite well", false), ("I would agree", true)]
Then in your tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell you could do this:
let cell = ChatCell.messageCell(withText: messages[indexPath.row].message, leading: messages[indexPath.row].leading)
return cell
Let me know if this works for you. I tested it in a Playground and it works as expected
Assuming that your configureChatCell is called from tableView:cellForRowAtIndexPath:, then #Paulw11 is right; cells are reused, so you should only make changes that are unique to that row in the table. In your example, the only calls that you should be making in your method are textView.text = text and the ones to resize the textView to fit. Everything else should go in a dynamic cell prototype in the storyboard or, if you want to do everything in code (which I have a bad feeling you do), then put the rest of the configuration in a UITableViewCell subclass, then register that subclass with your table view.
I write something like this. It's simple but can elucidate solution
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseid", forIndexPath: indexPath)
// Configure the cell...
let contentView = cell.contentView
let subviews = contentView.subviews
for view in subviews {
if 100 == view.tag {
let label = view as! UILabel
label.text = self.datas[indexPath.row]
} else if 200 == view.tag {
view.layer.cornerRadius = 10.0
}
}
return cell
}
the key point is config every thing in tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
the view in code with tag 200 is a background view has same frame with label, I use layout constraint in storyboard to make sure its size and position.

Resources