Related
I have UITableViewController, inside that, I added a button in the Cell.
Upon clicking the button, it shows a UIAlertController with "Ok" & "Cancel" Actions.
In my UIAlertController, I added a UIPickerView with some options.
When user clicks on the button in the cell, UIAlertController Popsup and shows the UIPickerView inside AlertBox with "OK" & "Cancel" actions.
Can you please help me in finding the value Selected by UIPickerView upon clicking the "Ok" button of UIAlertController.
namespace Navigation
{
public partial class ReviewInspectionViewController : UIViewController
{
private string filePath;
private InspectionData inspData = null;
List<ReportObservation> lstCriticalObs = new List<ReportObservation>();
List<ReportObservation> lstNonCriticalObs = new List<ReportObservation>();
public Audits Audit
{
get
{
return this._audit;
}
set
{
this._audit = value;
}
}
public ReviewInspectionViewController(IntPtr handle) : base(handle)
{
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
filePath = InProgressPath + Audit.AssessmentId.ToString() + ".xml";
inspData = ReadInspectionData(filePath);
if (inspData != null)
{
for (int i = 0; i < inspData.ReportSections.Count; i++)
{
lstCriticalObs.AddRange(inspData.ReportSections[i].ReportObservations.FindAll(x => x.Critical == true));
}
for (int i = 0; i < inspData.ReportSections.Count; i++)
{
lstNonCriticalObs.AddRange(inspData.ReportSections[i].ReportObservations.FindAll(x => x.Critical == false));
}
tblReviewInspection.Source = new ReviewInspectionSource(this, inspData, lstCriticalObs, lstNonCriticalObs);
tblReviewInspection.RowHeight = 160.0f;
}
}
}
class ReviewInspectionSource : UITableViewSource
{
NSString _cellID = new NSString("TableCell");
ReviewInspectionViewController _parent;
private InspectionData _inspectionData;
private List<ReportObservation> _lstCriticalObs;
private List<ReportObservation> _lstNonCriticalObs;
UITableView tvStatus;
public ReviewInspectionSource(ReviewInspectionViewController parent,
InspectionData inspectionData,
List<ReportObservation> lstCriticalObs,
List<ReportObservation> lstNonCriticalObs)
{
_inspectionData = inspectionData;
_lstCriticalObs = lstCriticalObs;
_lstNonCriticalObs = lstNonCriticalObs;
_parent = parent;
}
public override nint NumberOfSections(UITableView tableView)
{
return _lstCriticalObs.Count;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
int cnt = _lstCriticalObs[Convert.ToInt32(section)].ReportFindings.Count;
return cnt;
}
public override UIView GetViewForHeader(UITableView tableView, nint section)
{
UIView ctrls = new UIView();
tvStatus = new UITableView();
UILabel headerLabel = new UILabel(new CGRect(55, 4, 350, 33)); // Set the frame size you need
headerLabel.TextColor = UIColor.Red; // Set your color
headerLabel.Text = _lstCriticalObs[Convert.ToInt32(section)].ObsTitle;
UIButton btnEdit = new UIButton(new CGRect(5, 4, 50, 33));
var newFont = UIFont.SystemFontOfSize(17);
btnEdit.SetTitle("Edit", UIControlState.Normal);
btnEdit.Font = newFont;
btnEdit.BackgroundColor = UIColor.Yellow;
btnEdit.SetTitleColor(UIColor.Black, UIControlState.Normal);
btnEdit.SetTitleColor(UIColor.Black, UIControlState.Selected);
btnEdit.TouchUpInside += (object sender, EventArgs e) =>
{
tvStatus.Frame = new CGRect(((UIButton)sender).Frame.X, ((UIButton)sender).Frame.Y, 50, 230);
tvStatus.Hidden = false;
btnEdit.Hidden = true;
//string msg = "SectionIndex: " + section;
var Alert = UIAlertController.Create("Inspection", "\n\n\n\n\n\n", UIAlertControllerStyle.Alert);
Alert.ModalInPopover = true;
var picker = new UIPickerView(new CGRect(5, 20, 250, 140)) ;
picker.Model = new ObsStatusModel();
Alert.View.AddSubview(picker);
Alert.AddTextField((field) => {
field.Placeholder = "email address";
});
Alert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, (action) => {
}));
Alert.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, null ));
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(Alert, true, null);
/*
#IBAction func showChoices(_ sender: Any) {
let alert = UIAlertController(title: "Car Choices", message: "\n\n\n\n\n\n", preferredStyle: .alert)
alert.isModalInPopover = true
let pickerFrame = UIPickerView(frame: CGRect(x: 5, y: 20, width: 250, height: 140))
alert.view.addSubview(pickerFrame)
pickerFrame.dataSource = self
pickerFrame.delegate = self
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (UIAlertAction) in
print("You selected " + self.typeValue )
}))
self.present(alert,animated: true, completion: nil )
}
*/
};
List<string> lstStatus = new List<string>();
lstStatus.Add(" "); lstStatus.Add("IN"); lstStatus.Add("OUT"); lstStatus.Add("N/A"); lstStatus.Add("N/O");
tvStatus.Source = new StatusSource(lstStatus);
tvStatus.Layer.BorderColor = new CGColor(0, 0, 0);
tvStatus.Layer.BorderWidth = 2;
tvStatus.BackgroundColor = UIColor.White;
tvStatus.Hidden = true;
// tvStatus.Layer.ZPosition = 1;
tvStatus.ResignFirstResponder();
// btnEdit.BringSubviewToFront(tvStatus);
// btnEdit.Layer.ZPosition = 0;
//UILabel editLabel = new UILabel(new CGRect(360, 4, 100, 33)); // Set the frame size you need
//editLabel.TextColor = UIColor.Green; // Set your color
//editLabel.Text = "Edit Label";
ctrls.AddSubviews(headerLabel, btnEdit, tvStatus);
return ctrls;
}
public override nfloat GetHeightForHeader(UITableView tableView, nint section)
{
nfloat height = 50.0f;
return height;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
string msg = "Selected Row, ReviewInspection -- SectionIndex: " + indexPath.Section.ToString() + " RowIndex: " + indexPath.Row;
tvStatus.Hidden = true;
tvStatus.ResignFirstResponder();
//var Alert = UIAlertController.Create("Inspection", msg, UIAlertControllerStyle.Alert);
//Alert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, null));
//UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(Alert, true, null);
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
ReportFinding currentRequirementItem = _lstCriticalObs[indexPath.Section].ReportFindings[indexPath.Row]; // _inspectionData.ReportSections[indexPath.Row];
var cell = tableView.DequeueReusableCell(_cellID) as ReviewInspectionTableCell;
if (cell == null) { cell = new ReviewInspectionTableCell(_cellID); }
UIView customColorView = new UIView();
customColorView.BackgroundColor = UIColor.FromRGB(39, 159, 218);
UIView SelectedRowcustomColorView = new UIView();
SelectedRowcustomColorView.BackgroundColor = UIColor.FromRGB(255, 255, 255);
UIColor clrChecklistButton = UIColor.White;
UIColor clrFont = UIColor.White;
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
cell.PreservesSuperviewLayoutMargins = false;
cell.LayoutMargins = UIEdgeInsets.Zero;
cell.BackgroundView = customColorView;
cell.SelectedBackgroundView = SelectedRowcustomColorView;
cell.Edit.TouchUpInside += (object sender, EventArgs e) =>
{
string msg = "SectionIndex: " + indexPath.Section.ToString() + " RowIndex: " + indexPath.Row;
var Alert = UIAlertController.Create("Inspection", msg, UIAlertControllerStyle.Alert);
Alert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, null));
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(Alert, true, null);
};
cell.Delete.TouchUpInside += (object sender, EventArgs e) =>
{
string msg = "SectionIndex: " + indexPath.Section.ToString() + " RowIndex: " + indexPath.Row;
var Alert = UIAlertController.Create("Inspection", msg, UIAlertControllerStyle.Alert);
Alert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, null));
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(Alert, true, null);
};
cell.UpdateCellControlsWithAuditData(currentRequirementItem.ViolationText,
currentRequirementItem.FindingAnswer,
currentRequirementItem.RepeatViolation,
clrChecklistButton,
clrFont);
return cell;
}
}
public class ReviewInspectionTableCell : UITableViewCell
{
UILabel violationText, findingAnswer, repeatViolation;
UIButton btnEdit, btnDelete;
public ReviewInspectionTableCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
SelectionStyle = UITableViewCellSelectionStyle.Gray;
ContentView.BackgroundColor = UIColor.FromRGB(110, 214, 254);
ContentView.Layer.CornerRadius = 8;
var newFont = UIFont.SystemFontOfSize(17);
violationText = new UILabel();
violationText.Font = newFont;
violationText.LineBreakMode = UILineBreakMode.WordWrap;
violationText.TextAlignment = UITextAlignment.Left;
violationText.Lines = 0;
violationText.TextColor = UIColor.White;
violationText.HighlightedTextColor = UIColor.Green;
violationText.BackgroundColor = UIColor.Gray;
findingAnswer = new UILabel();
findingAnswer.Font = newFont;
findingAnswer.BackgroundColor = UIColor.Orange;
findingAnswer.LineBreakMode = UILineBreakMode.WordWrap;
findingAnswer.TextAlignment = UITextAlignment.Left;
findingAnswer.Lines = 0;
findingAnswer.TextColor = UIColor.White;
repeatViolation = new UILabel();
repeatViolation.Font = newFont;
repeatViolation.BackgroundColor = UIColor.Purple;
btnEdit = new UIButton();
btnEdit.Font = newFont;
btnEdit.SetTitle("Edit", UIControlState.Normal);
btnEdit.SetTitleColor(UIColor.Black, UIControlState.Selected);
btnDelete = new UIButton();
btnDelete.Font = newFont;
btnDelete.SetTitle("Delete", UIControlState.Normal);
btnDelete.SetTitleColor(UIColor.Black, UIControlState.Selected);
ContentView.AddSubviews(new UIView[] { violationText, findingAnswer, repeatViolation, btnEdit, btnDelete });
}
public UIButton Delete
{
get
{
return btnDelete;
}
}
public UIButton Edit
{
get
{
return btnEdit;
}
}
public void UpdateCellControlsWithAuditData(string _violationText,
string _findingAnswer,
Boolean _repeatVilation,
UIColor btnColor,
UIColor clrFont)
{
string repeat = string.Empty;
if (_repeatVilation)
{
repeat = "Repeat Violation.";
}
violationText.Text = _violationText;
findingAnswer.Text = _findingAnswer;
repeatViolation.Text = repeat;
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
//x, y, width, height
violationText.Frame = new CGRect(50, 0, 700, 80);
findingAnswer.Frame = new CGRect(50, 81, 700, 33);
repeatViolation.Frame = new CGRect(50, 110, 350, 33);
btnEdit.Frame = new CGRect(5, 0, 50, 33);
btnEdit.Layer.CornerRadius = 8;
btnEdit.BackgroundColor = UIColor.FromRGB(19, 120, 170);
btnDelete.Frame = new CGRect(5, 34, 50, 33);
btnDelete.Layer.CornerRadius = 8;
btnDelete.BackgroundColor = UIColor.FromRGB(19, 120, 170);
}
}
public class ObsStatusModel : UIPickerViewModel
{
static string[] names = new string[] {
"Monitor",
"Comprehensive",
"OutBreak Investigation",
"Complaint",
"FollowUp",
"PlanReview",
"Other"
};
public ObsStatusModel()
{
}
public override nint GetComponentCount(UIPickerView v)
{
return 1;
}
public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
return names.Length;
}
public override string GetTitle(UIPickerView picker, nint row, nint component)
{
return names[row];
}
}
class StatusSource : UITableViewSource
{
NSString _cellID = new NSString("TableCell");
private List<string> _lstStatus;
public StatusSource(List<string> lstStatus)
{
_lstStatus = lstStatus;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return _lstStatus.Count;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
var Alert = UIAlertController.Create("Inspection", string.Empty, UIAlertControllerStyle.Alert);
Alert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, null));
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(Alert, true, null);
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
string currentStatusItem = _lstStatus[indexPath.Row];
var cell = tableView.DequeueReusableCell(_cellID) as StatusTableCell;
if (cell == null) { cell = new StatusTableCell(_cellID); }
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
cell.PreservesSuperviewLayoutMargins = false;
cell.LayoutMargins = UIEdgeInsets.Zero;
cell.UpdateCellControlsWithAuditData(currentStatusItem);
return cell;
}
}
public class StatusTableCell : UITableViewCell
{
UILabel statusText;
public StatusTableCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
SelectionStyle = UITableViewCellSelectionStyle.Gray;
//ContentView.BackgroundColor = UIColor.FromRGB(110, 214, 254);
//ContentView.Layer.CornerRadius = 8;
var newFont = UIFont.SystemFontOfSize(17);
statusText = new UILabel();
statusText.Font = newFont;
statusText.LineBreakMode = UILineBreakMode.WordWrap;
statusText.TextAlignment = UITextAlignment.Center;
statusText.Lines = 0;
statusText.TextColor = UIColor.Black;
statusText.HighlightedTextColor = UIColor.Green;
statusText.BackgroundColor = UIColor.White;
ContentView.AddSubviews(new UIView[] { statusText });
}
public void UpdateCellControlsWithAuditData(string status)
{
statusText.Text = status;
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
//x, y, width, height
statusText.Frame = new CGRect(5, 5, 100, 25);
}
}
}
See attached image.
You can add a UIPickerView as subview on the view of UIAlertViewController. Refer to the following code:
public void CreatAlertView()
{
UIAlertController alert = UIAlertController.Create("Inspection\n\n\n\n\n\n\n", "", UIAlertControllerStyle.Alert);
alert.AddTextField((UITextField obj) =>
{
obj.Placeholder = "email address";
});
UIAlertAction OKAction = UIAlertAction.Create("OK", UIAlertActionStyle.Default, (obj) =>
{
if(alert.TextFields[0].Text.Length!=0)
{
//do some thing
}
});
UIAlertAction CancelAction = UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, null);
alert.AddAction(OKAction);
alert.AddAction(CancelAction);
var picker = new UIPickerView(new CGRect(0, 0,270,220));
picker.WeakDelegate = this;
picker.DataSource = this;
this.pickerView = picker;
alert.View.AddSubview(pickerView);
alertController = alert;
this.PresentViewController(alertController, false, null);
}
And don't forget to implement the methods in the delegate:
public nint GetComponentCount(UIPickerView pickerView)
{
return 1;
}
public nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
if (component==0)
{
return (System.nint)itemsArray.Length;
}
else
{
return 0;
}
}
[Export("pickerView:titleForRow:forComponent:")]
public string GetTitle(UIPickerView pickerView, nint row, nint component)
{
string content = itemsArray[(nuint)row].ToString();
return content;
}
[Export("pickerView:didSelectRow:inComponent:")]
public void Selected(UIPickerView pickerView, nint row, nint component)
{
string content= itemsArray[(nuint)row].ToString();
//do some thing
}
Then you can call the method
this.itemsArray= new string[] {"apple","google","apple","google","apple","google"} ;//init the source array
CreatAlertView();
I have upload the demo to my git. you can download it for more details.demo
As we can see on the bottom of the screen, there are options to be chosen.
Which other options are there for this menu such that I can display longer strings fully?
For your requirement you have to make a customize one to do it:
Try to use this code:
using System;
using System.Collections.Generic;
using CoreGraphics;
using UIKit;
namespace CusActionSheet
{
public delegate void OneParameterEventHandler<T>(T data);
public class ALActionSheet : UIView
{
private nfloat maximumProportion = 0.8f;
public event OneParameterEventHandler<string> RowClicked;
//You also can use this if you don't know the generic(but remeber also change it for source):
//public delegate void OneParameterEventHandler(string data);
//public event OneParameterEventHandler SelectedPhoneNumber;
private UIView backgroundContainer;
private UIView topContainer;
private UILabel topContainerTitleView;
private UITableView topContainerTable;
private ALActionSheetSource topContainerTableSource;
private UIButton btnCancel;
private nfloat itemPadding = 10;
private nfloat itemHeight = 60;
private nfloat cornerRadius = 10;
private List<string> dataList;
private CGRect backContainerShownFrame;
private CGRect backContainerHiddenFrame;
private string selectedNumber;
public CGRect BackContainerShownFrame
{
get
{
return backContainerShownFrame;
}
set
{
backContainerShownFrame = value;
}
}
public ALActionSheet(List<string> _dataList)
{
this.BackgroundColor = UIColor.Clear;
this.dataList = _dataList;
backgroundContainer = new UIView();
backgroundContainer.Layer.CornerRadius = cornerRadius;
backgroundContainer.Layer.MasksToBounds = true;
topContainer = new UIView();
topContainer.BackgroundColor = UIColor.White;
topContainer.Layer.CornerRadius = cornerRadius;
topContainer.Layer.MasksToBounds = true;
topContainerTitleView = new UILabel();
topContainerTitleView.Text = "Select a number";
topContainerTitleView.TextAlignment = UITextAlignment.Center;
topContainerTitleView.BackgroundColor = UIColor.FromRGB(230, 230, 230);
topContainer.AddSubview(topContainerTitleView);
topContainerTableSource = new ALActionSheetSource(dataList);
topContainerTableSource.RowClicked += (data) =>
{
selectedNumber = data;
Close();
};
topContainerTable = new UITableView();
topContainerTable.RowHeight = itemHeight;
topContainerTable.Layer.CornerRadius = cornerRadius;
topContainerTable.Layer.MasksToBounds = true;
topContainerTable.BackgroundColor = UIColor.White;
topContainerTable.Source = topContainerTableSource;
topContainer.AddSubview(topContainerTable);
btnCancel = new UIButton(UIButtonType.System);
btnCancel.SetTitle("Cancel", UIControlState.Normal);
btnCancel.TitleLabel.Font = UIFont.SystemFontOfSize(18);
btnCancel.BackgroundColor = UIColor.White;
btnCancel.Layer.CornerRadius = cornerRadius;
btnCancel.Layer.MasksToBounds = true;
btnCancel.TouchUpInside += delegate
{
Close();
};
backgroundContainer.AddSubview(topContainer);
backgroundContainer.AddSubview(btnCancel);
Layout();
}
public void Layout()
{
this.Frame = UIScreen.MainScreen.Bounds;
nfloat tableMaxHeight = UIScreen.MainScreen.Bounds.Height * maximumProportion - 2 * itemPadding - 2 * itemHeight;
nfloat tableHeight = dataList.Count * itemHeight < tableMaxHeight ? dataList.Count * itemHeight : tableMaxHeight;
nfloat itemWidth = UIScreen.MainScreen.Bounds.Width - 2 * itemPadding;
nfloat backgroundContainerHeight = tableHeight + 2 * itemHeight + itemPadding;
backgroundContainer.Frame = new CGRect(0, 0, itemWidth, backgroundContainerHeight);
nfloat topContainerHeight = itemHeight + tableHeight;
topContainer.Frame = new CGRect(0, 0, itemWidth, topContainerHeight);
topContainerTitleView.Frame = new CGRect(0, 0, itemWidth, itemHeight);
topContainerTable.Frame = new CGRect(0, itemHeight, itemWidth, tableHeight);
btnCancel.Frame = new CGRect(0, topContainerHeight + itemPadding, itemWidth, itemHeight);
BackContainerShownFrame = new CGRect(new CGPoint(itemPadding, UIScreen.MainScreen.Bounds.Height - backgroundContainerHeight - itemPadding), backgroundContainer.Bounds.Size);
backContainerHiddenFrame = new CGRect(new CGPoint(itemPadding, UIScreen.MainScreen.Bounds.Height + itemPadding), backgroundContainer.Bounds.Size);
}
public void Show()
{
UIApplication.SharedApplication.KeyWindow.AddSubview(this);
backgroundContainer.Frame = backContainerHiddenFrame;
this.AddSubview(backgroundContainer);
UIView.Animate(0.3, delegate
{
this.BackgroundColor = UIColor.FromRGBA(0, 0, 0, 0.5f);
backgroundContainer.Frame = BackContainerShownFrame;
});
}
public void Close()
{
UIView.Animate(0.3, delegate
{
backgroundContainer.Frame = backContainerHiddenFrame;
this.BackgroundColor = UIColor.Clear;
}, delegate
{
this.RemoveFromSuperview();
if (null != RowClicked && null != selectedNumber)
RowClicked(selectedNumber);
});
}
}
class ALActionSheetSource : UITableViewSource
{
public event OneParameterEventHandler<string> RowClicked;
private string cellID = "ALActionSheetCell";
private List<string> dataList;
public ALActionSheetSource(List<string> _dataList)
{
dataList = _dataList;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return dataList.Count;
}
public override UITableViewCell GetCell(UITableView tableView, Foundation.NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell(cellID);
if (null == cell)
{
cell = new UITableViewCell(UITableViewCellStyle.Default, cellID);
cell.TextLabel.TextAlignment = UITextAlignment.Center;
//cell.TextLabel.AdjustsFontSizeToFitWidth = true;//Auto resize by content
cell.TextLabel.Lines = int.MaxValue;//Multiple lines
}
cell.TextLabel.Text = dataList[indexPath.Row];
return cell;
}
public override void RowSelected(UITableView tableView, Foundation.NSIndexPath indexPath)
{
if (null != RowClicked)
RowClicked(dataList[indexPath.Row]);
}
}
}
And invoke it like this:
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
List<string> testList = new List<string>();
for (int i = 0; i < 10; i++) {
testList.Add("Item " + i.ToString());
}
testList.Add("I'm a very very very very very very very very long text.");
ALActionSheet actionSheet = new ALActionSheet(testList);
actionSheet.RowClicked += (data) => {
Console.WriteLine("data = "+data);
};
UIButton btnTest = new UIButton(UIButtonType.System);
btnTest.SetTitle("Test", UIControlState.Normal);
btnTest.Frame = new CoreGraphics.CGRect(50, 50, 80, 30);
btnTest.TouchUpInside += delegate {
actionSheet.Show();
};
this.Add(btnTest);
}
Hope it can help you.
I use UICollectionView and refer to this link implement scroll table view Fixed single column and row like below image.
but when I want to add shadow property, it display messy.
I think the reason is that I reuse cell when render visual view, but I don't know how to fix it :(
provide my code below, thanks for your time!
import Foundation
import SwiftyJSON
#objc(RNCollection)
class RNCollection : UICollectionView, UICollectionViewDataSource, UICollectionViewDelegate {
var contentCellIdentifier = "CellIdentifier"
var collectionView: UICollectionView!
static var dataSource = []
static var sections = 0
static var rows = 0
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: CGRectZero, collectionViewLayout: RNCollectionLayout())
self.registerClass(RNCollectionCell.self, forCellWithReuseIdentifier: contentCellIdentifier)
self.directionalLockEnabled = false
self.backgroundColor = UIColor.whiteColor()
self.delegate = self
self.dataSource = self
self.frame = frame
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setConfig(config: String!) {
var json: JSON = nil;
if let data = config.dataUsingEncoding(NSUTF8StringEncoding) {
json = JSON(data: data);
};
RNCollection.dataSource = json["dataSource"].arrayObject!
RNCollection.sections = json["sections"].intValue
RNCollection.rows = json["rows"].intValue
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let contentCell: RNCollectionCell = collectionView.dequeueReusableCellWithReuseIdentifier(contentCellIdentifier, forIndexPath: indexPath) as! RNCollectionCell
contentCell.textLabel.textColor = UIColor.blackColor()
if indexPath.section == 0 {
contentCell.backgroundColor = UIColor(red: 232/255.0, green: 232/255.0, blue: 232/255.0, alpha: 1.0)
if indexPath.row == 0 {
contentCell.textLabel.font = UIFont.systemFontOfSize(16)
contentCell.textLabel.text = (RNCollection.dataSource[0] as! NSArray)[0] as? String
} else {
contentCell.textLabel.font = UIFont.systemFontOfSize(14)
contentCell.textLabel.text = (RNCollection.dataSource[0] as! NSArray)[indexPath.row] as? String
}
} else {
contentCell.textLabel.font = UIFont.systemFontOfSize(12)
contentCell.backgroundColor = UIColor.whiteColor()
if(indexPath.section % 2 == 0) {
contentCell.backgroundColor = UIColor(red: 234/255.0, green: 234/255.0, blue: 236/255.0, alpha: 1.0)
}
if indexPath.row == 0 {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[0] as? String
contentCell.layer.shadowOffset = CGSize(width: 3, height: 3)
contentCell.layer.shadowOpacity = 0.7
contentCell.layer.shadowRadius = 2
} else {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[indexPath.row] as? String
}
}
return contentCell
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return RNCollection.rows
}
// MARK - UICollectionViewDataSource
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return RNCollection.sections
}
}
You are specifying your customizations for a certain section/row and in your else condition you just set the text. You should specify your required/default customization in your else condition. Consider this part of your code
if indexPath.row == 0 {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[0] as? String
contentCell.layer.shadowOffset = CGSize(width: 3, height: 3)
contentCell.layer.shadowOpacity = 0.7
contentCell.layer.shadowRadius = 2
} else {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[indexPath.row] as? String
}
You have specified your shadowOffset,shadowOpacity and shadowRadius in your if condition, but you ignored them in your else condition. If you specify the appearance in the else condition you won't have the problem you are facing now. It should look something like this
if indexPath.row == 0 {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[0] as? String
contentCell.layer.shadowOffset = CGSize(width: 3, height: 3)
contentCell.layer.shadowOpacity = 0.7
contentCell.layer.shadowRadius = 2
} else {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[indexPath.row] as? String
contentCell.layer.shadowOffset = 0
contentCell.layer.shadowOpacity = 0
contentCell.layer.shadowRadius = 0
}
If you reset everything properly in all your condition checks, this problem won't occur.
You set up shadow when your row is equal to 0:
if indexPath.row == 0 {...}
but if the row is not equal to zero collection view can reuse the cell when the shadow was already set. The solution is reset the shadow when row is not 0, in your code you can do something like this (see todo):
if indexPath.row == 0 { // Set up shadow here
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[0] as? String
contentCell.layer.shadowOffset = CGSize(width: 3, height: 3)
contentCell.layer.shadowOpacity = 0.7
contentCell.layer.shadowRadius = 2
} else {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[indexPath.row] as? String
//TODO: Remove the shadow here
}
If you wanna save the cell setting everytime when your cell appears,you should set every possible condition of the method "func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell".Do not prevent the reuse of collectionView only if your data or items is few.
In Objective-C, but in Swift, the logic should be the same and not that hard to translate.
In RNCollectionCell.
//These enums may have a better nam, I agree
typedef enum : NSUInteger {
RNCollectionCellStyleColumnTitle,
RNCollectionCellStyleColumnValue,
RNCollectionCellStyleRowTitle,
RNCollectionCellStyleUpperLeftCorner,
} RNCollectionCellStyle;
-(void)titleIt:(NSString *)title forStyle:(RNCollectionCellStyle)style forSection:(NSUInteger)section
{
self.textLabel.textColor = [UIColor blackColor]; //Since it's always the same could be done elsewhere once for all.
//Note I didn't check completely if the case were good, and I didn't add the background color logic, but it should be done here too.
switch (style)
{
case RNCollectionCellStyleColumnTitle:
{
self.textLabel.font = [UIFont systemFontOfSize:12];
self.layer.shadowOffset = CGSizeMake(3, 3);
self.layer.shadowOpacity = 0.7;
self.layer.shadowRadius = 2;
}
break;
case RNCollectionCellStyleColumnValue:
{
self.textLabel.font = [UIFont systemFontOfSize:12];
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowOpacity = 0;
self.layer.shadowRadius = 0;
}
break;
case RNCollectionCellStyleRowTitle:
{
self.textLabel.font = [UIFont systemFontOfSize:14];
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowOpacity = 0;
self.layer.shadowRadius = 0;
}
break;
case RNCollectionCellStyleUpperLeftCorner:
{
self.textLabel.font = [UIFont systemFontOfSize:16];
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowOpacity = 0;
self.layer.shadowRadius = 0;
}
break;
default:
break;
}
self.textLabel.text = title;
}
-(void)prepareForReuse
{
[super prepareForReuse];
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowOpacity = 0;
self.layer.shadowRadius = 0;
}
In RNCollection:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath
{
RNCollectionCell *contentCell = [collectionView dequeueReusableCellWithReuseIdentifier: contentCellIdentifier forIndexPath:indexPath];
//Here check what style you need to do
//Also, you seems to always do dataSource[indexPath.section][indexPath.row], you just use the "if test values".
[contentCell titleIt:dataSource[indexPath.section][indexPath.row]
forStyle:RNCollectionCellStyleColumnTitle
forSection:[indexPath section]];
return contentCell;
}
As said, I didn't check if all the cases were correct according to your needs.
But, you don't "prevent reuse of cell", it's optimized.
You need to "reset" values before reused or when you set the values. In theory the thing done in prepareForReuse should be enough and you shouldn't have to "reset" the layer effect in the switch, but I honestly didn't check it.
I am trying to hide and display section headers when there is or isn't data populating the tableview. The code I have now works as intended occasionally, but mostly, one or both of the section headers will remain displayed, (However, if I drag the header off screen when there is no data it will disappear as intended).
This is the code I am using to hide/unhide the sections.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
//Create custom header titles for desired header spacing
if (section == 0) {
UIView *headerView;
if (self.searchUsers.count || self.searchTracks.count)
{
headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
}
else{
headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 0, 0)];
}
HeaderTitleLabel *headerTitle = [[HeaderTitleLabel alloc] initWithFrame:CGRectMake(17, 10, 150, 20) headerText:#"USERS"];
[headerView addSubview:headerTitle];
return headerView;
}
else if (section == 1){
UIView *headerView;
if (self.searchUsers.count || self.searchTracks.count)
{
headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
}
else{
headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 0, 0)];
}
HeaderTitleLabel *headerTitle = [[HeaderTitleLabel alloc] initWithFrame:CGRectMake(17, 0, 150, 20) headerText:#"SONGS"];
[headerView addSubview:headerTitle];
return headerView;
}
else
return nil;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
//custom header spacing
if (section == 0) {
if (self.searchUsers.count || self.searchTracks.count)
{
return 40;
}
else
return 0;
}
else if (section == 1) {
if (self.searchUsers.count || self.searchTracks.count)
{
return 50;
}
else
return 0;
}
else
return 0;
}
I check if my array has objects, if not then set the frame's height to 0. This doesn't seem to be working though. Any ideas how I should go about doing this? Thank you.
Your model should drive your table view. Don't just return a 'number of rows' value from your model, but also a 'number of sections' value.
Consider an array that contains two arrays as your model.
You are using wrong approach of table view. Can you clarify what do you exactly want to do. I could help you. Rather then UIView use UITableView Cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.searchUsers.count || self.searchTracks.count)
{
if (indexPath.section ==0) {
static NSString *simpleTableIdentifier = #"CategoryCell";
CategoryCell *cell = (CategoryCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CategoryCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.lbl_CatName.text = [[self.CategoryArray objectAtIndex:indexPath.row] objectForKey:kNAME];
return cell;}
else if (indexPath.section ==1){
static NSString *EarnPointIdentifier = #"EarnPointTableCell";
EarnPointTableCell *EarnPointcell = (EarnPointTableCell *)[tableView dequeueReusableCellWithIdentifier:EarnPointIdentifier];
if (EarnPointcell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"EarnPointTableCell" owner:self options:nil];
EarnPointcell = [nib objectAtIndex:0];
}
return EarnPointcell ;
}
else{
//it will have transparent background and every thing will be transparent
static NSString *RewardIdentifier = #"RewardTableCell";
RewardTableCell *RewardCell = (RewardTableCell *)[tableView dequeueReusableCellWithIdentifier:RewardIdentifier];
if (RewardCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"RewardTableCell" owner:self options:nil];
RewardCell = [nib objectAtIndex:0];
}
return RewardCell ;
}
}
}
It seems to be working fine now. What I did was create boolean values to keep track of my data when my data source is nil. Like so;
[SoundCloudAPI getTracksWithSearch:userInput userID:nil withCompletion:^(NSMutableArray *tracks) {
self.searchTracks = tracks;
[self.tableView beginUpdates];
if (tracks.count) {
self.songsHeaderIsHidden = NO;
}
else
self.songsHeaderIsHidden = YES;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
[[self.tableView headerViewForSection:1] setNeedsLayout];
[[self.tableView headerViewForSection:1] setNeedsDisplay];
[self.tableView endUpdates];
}];
Then set the header accordingly..
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
//Create custom header titles for desired header spacing
if (section == 0) {
if (self.userHeaderIsHidden) {
return nil;
}
else {
UIView *userHeaderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
HeaderTitleLabel *usersHeaderTitle = [[HeaderTitleLabel alloc] initWithFrame:CGRectMake(17, 10, 150, 20) headerText:#"USERS"];
[userHeaderView addSubview:usersHeaderTitle];
return userHeaderView;
}
}
else if (section == 1){
if (self.songsHeaderIsHidden) {
return nil;
}
else {
UIView *songsHeaderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
songsHeaderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
HeaderTitleLabel *songsHeaderTitle = [[HeaderTitleLabel alloc] initWithFrame:CGRectMake(17, 0, 150, 20) headerText:#"SONGS"];
[songsHeaderView addSubview:songsHeaderTitle];
return songsHeaderView;
}
}
else
return nil;
}
Personally, I would use UICollectionView and write your own layout. That way you can explicitly layout where the section headers will be at all times. The best approach is to probably subclass UICollectionViewFlowLayout because you get all the superclass properties for free (such as itemSize, headerReferenceSize, etc.)
Here's an implementation I wrote for having sticky headers, like the Instagram app.
Notice that in prepareLayout, I just calculate the normal positions of every element. Then in layoutAttributesForElementInRect I go through and figure out where to place the header. This is where you can go through and see if the header should be shown or not.
import UIKit
class BoardLayout: UICollectionViewFlowLayout {
private var sectionFrames: [CGRect] = []
private var headerFrames: [CGRect] = []
private var footerFrames: [CGRect] = []
private var layoutAttributes: [UICollectionViewLayoutAttributes] = []
private var contentSize: CGSize = CGSizeZero
override func prepareLayout() {
super.prepareLayout()
self.sectionFrames.removeAll(keepCapacity: false)
self.headerFrames.removeAll(keepCapacity: false)
self.footerFrames.removeAll(keepCapacity: false)
self.layoutAttributes.removeAll(keepCapacity: false)
let sections = self.collectionView?.numberOfSections()
var yOffset: CGFloat = 0.0
for var i: Int = 0; i < sections; i++ {
let indexPath = NSIndexPath(forItem: 0, inSection: i)
var itemSize: CGSize = self.itemSize
if let d = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout {
if let collection = self.collectionView {
if let size = d.collectionView?(collection, layout: self, sizeForItemAtIndexPath: NSIndexPath(forItem: 0, inSection: i)) {
itemSize = size
}
}
}
let headerFrame = CGRect(x: 0, y: yOffset, width: self.headerReferenceSize.width, height: self.headerReferenceSize.height)
self.headerFrames.append(headerFrame)
var headerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withIndexPath: indexPath)
headerAttribute.frame = headerFrame
headerAttribute.zIndex = 1000
self.layoutAttributes.append(headerAttribute)
yOffset += self.headerReferenceSize.height - 1 // - 1 so that the bottom border of the header and top border of the cell line up
let sectionFrame = CGRect(x: 0, y: yOffset, width: itemSize.width, height: itemSize.height)
self.sectionFrames.append(sectionFrame)
var sectionAttribute = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
sectionAttribute.frame = sectionFrame
self.layoutAttributes.append(sectionAttribute)
yOffset += itemSize.height
let footerFrame = CGRect(x: 0, y: yOffset, width: self.footerReferenceSize.width, height: self.footerReferenceSize.height)
self.footerFrames.append(footerFrame)
var footerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withIndexPath: indexPath)
footerAttribute.frame = footerFrame
self.layoutAttributes.append(footerAttribute)
yOffset += self.minimumLineSpacing + self.footerReferenceSize.height
}
self.contentSize = CGSize(width: self.collectionView!.width, height: yOffset)
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
var newAttributes: [UICollectionViewLayoutAttributes] = []
for attributes in self.layoutAttributes {
let frame = attributes.frame
if !CGRectIntersectsRect(frame, CGRect(x: 0, y: rect.origin.y, width: rect.size.width, height: rect.size.height)) {
continue
}
let indexPath = attributes.indexPath
let section = indexPath.section
if let kind = attributes.representedElementKind {
if kind == UICollectionElementKindSectionHeader {
var headerFrame = attributes.frame
let footerFrame = self.footerFrames[section]
if CGRectGetMinY(headerFrame) <= self.collectionView!.contentOffset.y {
headerFrame.origin.y = self.collectionView!.contentOffset.y
}
if CGRectGetMinY(headerFrame) >= CGRectGetMinY(footerFrame) {
headerFrame.origin.y = footerFrame.origin.y
}
attributes.frame = headerFrame
self.headerFrames[section] = headerFrame
} else if kind == UICollectionElementKindSectionFooter {
attributes.frame = self.footerFrames[section]
}
} else {
attributes.frame = self.sectionFrames[section]
}
newAttributes.append(attributes)
}
return newAttributes
}
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
let frame = self.sectionFrames[indexPath.item]
let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)
attributes.frame = frame
return attributes
}
override func layoutAttributesForSupplementaryViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
let attributes = super.layoutAttributesForSupplementaryViewOfKind(elementKind, atIndexPath: indexPath)
if elementKind == UICollectionElementKindSectionHeader {
attributes.frame = self.headerFrames[indexPath.section]
} else if elementKind == UICollectionElementKindSectionFooter {
attributes.frame = self.footerFrames[indexPath.section]
}
return attributes
}
override func collectionViewContentSize() -> CGSize {
return self.contentSize
}
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
}
I'd recommend using the UICollectionViewDelegateFlowLayout methods so you can dynamically determine the reference size of the header.
I have a tableview, where sometimes there might not be any results to list, so I would like to put something up that says "no results" if there are no results (either a label or one table view cell?).
Is there an easiest way to do this?
I would try a label behind the tableview then hide one of the two based on the results, but since I'm working with a TableViewController and not a normal ViewController I'm not sure how smart or doable that is.
I'm also using Parse and subclassing as a PFQueryTableViewController:
#interface TableViewController : PFQueryTableViewController
I can provide any additional details needed, just let me know!
TableViewController Scene in Storyboard:
EDIT: Per Midhun MP, here's the code I'm using
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSInteger numOfSections = 0;
if ([self.stringArray count] > 0)
{
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
numOfSections = 1;
//yourTableView.backgroundView = nil;
self.tableView.backgroundView = nil;
}
else
{
UILabel *noDataLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height)];
noDataLabel.text = #"No data available";
noDataLabel.textColor = [UIColor blackColor];
noDataLabel.textAlignment = NSTextAlignmentCenter;
//yourTableView.backgroundView = noDataLabel;
//yourTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundView = noDataLabel;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}
return numOfSections;
}
And here's the View I'm getting, it still has separator lines. I get the feeling that this is some small change, but I'm not sure why separator lines are showing up?
You can easily achieve that by using backgroundView property of UITableView.
Objective C:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSInteger numOfSections = 0;
if (youHaveData)
{
yourTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
numOfSections = 1;
yourTableView.backgroundView = nil;
}
else
{
UILabel *noDataLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, yourTableView.bounds.size.width, yourTableView.bounds.size.height)];
noDataLabel.text = #"No data available";
noDataLabel.textColor = [UIColor blackColor];
noDataLabel.textAlignment = NSTextAlignmentCenter;
yourTableView.backgroundView = noDataLabel;
yourTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}
return numOfSections;
}
Swift:
func numberOfSections(in tableView: UITableView) -> Int
{
var numOfSections: Int = 0
if youHaveData
{
tableView.separatorStyle = .singleLine
numOfSections = 1
tableView.backgroundView = nil
}
else
{
let noDataLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
noDataLabel.text = "No data available"
noDataLabel.textColor = UIColor.black
noDataLabel.textAlignment = .center
tableView.backgroundView = noDataLabel
tableView.separatorStyle = .none
}
return numOfSections
}
Reference UITableView Class Reference
backgroundView Property
The background view of the table view.
Declaration
Swift
var backgroundView: UIView?
Objective-C
#property(nonatomic, readwrite, retain) UIView *backgroundView
Discussion
A table view’s background view is automatically resized to match the
size of the table view. This view is placed as a subview of the table
view behind all cells, header views, and footer views.
You must set this property to nil to set the background color of the
table view.
For Xcode 8.3.2 - Swift 3.1
Here is a not-so-well-known but incredibly easy way to achieve adding a "No Items" view to an empty table view that goes back to Xcode 7. I'll leave it to you control that logic that adds/removes the view to the table's background view, but here is the flow for and Xcode (8.3.2) storyboard:
Select the scene in the Storyboard that has your table view.
Drag an empty UIView to the "Scene Dock" of that scene
Add a UILabel and any constraints to the new view and then create an IBOutlet for that view
Assign that view to the tableView.backgroundView
Behold the magic!
Ultimately this works anytime you want to add a simple view to your view controller that you don't necessarily want to be displayed immediately, but that you also don't want to hand code.
Swift Version of above code :-
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
var numOfSection: NSInteger = 0
if CCompanyLogoImage.count > 0 {
self.tableView.backgroundView = nil
numOfSection = 1
} else {
var noDataLabel: UILabel = UILabel(frame: CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height))
noDataLabel.text = "No Data Available"
noDataLabel.textColor = UIColor(red: 22.0/255.0, green: 106.0/255.0, blue: 176.0/255.0, alpha: 1.0)
noDataLabel.textAlignment = NSTextAlignment.Center
self.tableView.backgroundView = noDataLabel
}
return numOfSection
}
But If you are loading Information From a JSON , you need to check whether the JSON is empty or not , therefor if you put code like this it initially shows "No data" Message then disappear. Because after the table reload data the message hide. So You can put this code where load JSON data to an array. SO :-
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func extract_json(data:NSData) {
var error: NSError?
let jsonData: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers , error: &error)
if (error == nil) {
if let jobs_list = jsonData as? NSArray
{
if jobs_list.count == 0 {
var noDataLabel: UILabel = UILabel(frame: CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height))
noDataLabel.text = "No Jobs Available"
noDataLabel.textColor = UIColor(red: 22.0/255.0, green: 106.0/255.0, blue: 176.0/255.0, alpha: 1.0)
noDataLabel.textAlignment = NSTextAlignment.Center
self.tableView.backgroundView = noDataLabel
}
for (var i = 0; i < jobs_list.count ; i++ )
{
if let jobs_obj = jobs_list[i] as? NSDictionary
{
if let vacancy_title = jobs_obj["VacancyTitle"] as? String
{
CJobTitle.append(vacancy_title)
if let vacancy_job_type = jobs_obj["VacancyJobType"] as? String
{
CJobType.append(vacancy_job_type)
if let company_name = jobs_obj["EmployerCompanyName"] as? String
{
CCompany.append(company_name)
if let company_logo_url = jobs_obj["EmployerCompanyLogo"] as? String
{
//CCompanyLogo.append("http://google.com" + company_logo_url)
let url = NSURL(string: "http://google.com" + company_logo_url )
let data = NSData(contentsOfURL:url!)
if data != nil {
CCompanyLogoImage.append(UIImage(data: data!)!)
}
if let vacancy_id = jobs_obj["VacancyID"] as? String
{
CVacancyId.append(vacancy_id)
}
}
}
}
}
}
}
}
}
do_table_refresh();
}
func do_table_refresh() {
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
}
You can try this control. Its is pretty neat. DZNEmptyDataSet
Or if I were you all I would do is
Check to see if your data array is empty
If it is empty then add one object called #"No Data" to it
Display that string in cell.textLabel.text
Easy peasy
Swift 3 (updated):
override func numberOfSections(in tableView: UITableView) -> Int {
if myArray.count > 0 {
self.tableView.backgroundView = nil
self.tableView.separatorStyle = .singleLine
return 1
}
let rect = CGRect(x: 0,
y: 0,
width: self.tableView.bounds.size.width,
height: self.tableView.bounds.size.height)
let noDataLabel: UILabel = UILabel(frame: rect)
noDataLabel.text = "Custom message."
noDataLabel.textColor = UIColor.white
noDataLabel.textAlignment = NSTextAlignment.center
self.tableView.backgroundView = noDataLabel
self.tableView.separatorStyle = .none
return 0
}
Swift3.0
I hope it server your purpose......
In your UITableViewController .
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != "" {
if filteredContacts.count > 0 {
self.tableView.backgroundView = .none;
return filteredContacts.count
} else {
Helper.EmptyMessage(message: ConstantMap.NO_CONTACT_FOUND, viewController: self)
return 0
}
} else {
if contacts.count > 0 {
self.tableView.backgroundView = .none;
return contacts.count
} else {
Helper.EmptyMessage(message: ConstantMap.NO_CONTACT_FOUND, viewController: self)
return 0
}
}
}
Helper Class with function :
/* Description: This function generate alert dialog for empty message by passing message and
associated viewcontroller for that function
- Parameters:
- message: message that require for empty alert message
- viewController: selected viewcontroller at that time
*/
static func EmptyMessage(message:String, viewController:UITableViewController) {
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: viewController.view.bounds.size.width, height: viewController.view.bounds.size.height))
messageLabel.text = message
let bubbleColor = UIColor(red: CGFloat(57)/255, green: CGFloat(81)/255, blue: CGFloat(104)/255, alpha :1)
messageLabel.textColor = bubbleColor
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = .center;
messageLabel.font = UIFont(name: "TrebuchetMS", size: 18)
messageLabel.sizeToFit()
viewController.tableView.backgroundView = messageLabel;
viewController.tableView.separatorStyle = .none;
}
I think the most elegant way to solve your problem is switching from a UITableViewController to a UIViewController that contains a UITableView. This way you can add whatever UIView you want as subviews of the main view.
I wouldn't recommend using a UITableViewCell to do this you might need to add additional things in the future and things can quicky get ugly.
You can also do something like this, but this isn't the best solution either.
UIWindow* window = [[UIApplication sharedApplication] keyWindow];
[window addSubview: OverlayView];
Use this code in Your numberOfSectionsInTableView method:-
if ([array count]==0
{
UILabel *fromLabel = [[UILabel alloc]initWithFrame:CGRectMake(50, self.view.frame.size.height/2, 300, 60)];
fromLabel.text =#"No Result";
fromLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
fromLabel.backgroundColor = [UIColor clearColor];
fromLabel.textColor = [UIColor lightGrayColor];
fromLabel.textAlignment = NSTextAlignmentLeft;
[fromLabel setFont:[UIFont fontWithName:Embrima size:30.0f]];
[self.view addSubview:fromLabel];
[self.tblView setHidden:YES];
}
I would present a an overlay view that has the look and message you want if the tableview has no results. You could do it in ViewDidAppear, so you have the results before showing/not showing the view.
SWIFT 3
let noDataLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
noDataLabel.text = "No data available"
noDataLabel.textColor = UIColor.white
noDataLabel.font = UIFont(name: "Open Sans", size: 15)
noDataLabel.textAlignment = .center
tableView.backgroundView = noDataLabel
tableView.separatorStyle = .none
If you don't use the tableview footer and do not want the tableview to fill up the screen with empty default table cells i would suggest that you set your tableview footer to an empty UIView. I do not know the correct syntax for doing this in obj-c or Swift, but in Xamarin.iOS i would do it like this:
public class ViewController : UIViewController
{
UITableView _table;
public ViewController (IntPtr handle) : base (handle)
{
}
public override void ViewWillAppear(bool animated) {
// Initialize table
_table.TableFooterView = new UIView();
}
}
Above code will result in a tableview without the empty cells
Here is the solution that worked for me.
Add the following code to a new file.
Change your table class to the custom class "MyTableView" from storyboard or .xib
(this will work for the first section only. If you want to customize more, do changes in the MyTableView reloadData() function accordingly for other sections)
public class MyTableView: UITableView {
override public func reloadData() {
super.reloadData()
if self.numberOfRows(inSection: 0) == 0 {
if self.viewWithTag(1111) == nil {
let noDataLabel = UILabel()
noDataLabel.textAlignment = .center
noDataLabel.text = "No Data Available"
noDataLabel.tag = 1111
noDataLabel.center = self.center
self.backgroundView = noDataLabel
}
} else {
if self.viewWithTag(1111) != nil {
self.backgroundView = nil
}
}
}
}
If you want to do this without any code, try this!
Click on your tableView.
Change the style from "plain" to "grouped".
Now when you use ....
tableView.backgroundView = INSERT YOUR LABEL OR VIEW
It will not show the separators!
Add this code in one file and change your collection type to CustomCollectionView
import Foundation
class CustomCollectionView: UICollectionView {
var emptyModel = EmptyMessageModel()
var emptyView: EmptyMessageView?
var showEmptyView: Bool = true
override func reloadData() {
super.reloadData()
emptyView?.removeFromSuperview()
self.backgroundView = nil
if !showEmptyView {
return
}
if numberOfSections < 1 {
let rect = CGRect(x: 0,
y: 0,
width: self.bounds.size.width,
height: self.bounds.size.height)
emptyView = EmptyMessageView()
emptyView?.frame = rect
if let emptyView = emptyView {
// self.addSubview(emptyView)
self.backgroundView = emptyView
}
emptyView?.setView(with: emptyModel)
} else {
emptyView?.removeFromSuperview()
self.backgroundView = nil
}
}
}
class EmptyMessageView: UIView {
#IBOutlet weak var messageLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "EmptyMessageView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
func setView(with model: EmptyMessageModel) {
messageLabel.text = model.message ?? ""
imageView.image = model.image ?? #imageLiteral(resourceName: "no_notification")
}
}
///////////
class EmptyMessageModel {
var message: String?
var image: UIImage?
init(message: String = "No data available", image: UIImage = #imageLiteral(resourceName: "no_notification")) {
self.message = message
self.image = image
}
}