Present popover from cells accessory button? - ios

I'm having a hard time presenting a popover correctly from the accessoryButton of a tableviewCell.
The reason I'm not using accessory view is because the cell is in edit mode and I couldn't display both the green plus sign + custom accessory view.. Maybe I overlooked something on that front?
Currently my popover shows correctly, but that's only the case for this configuration since I set a static distance from the origin... Any Ideas how to solve this?
Code:
-(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if (![self duplicateDayContent]) {
duplicateDayContent = [[self storyboard]instantiateViewControllerWithIdentifier:#"CopyDay"];
[duplicateDayContent setDelegate:self];
duplicateDayPopover = [[UIPopoverController alloc]initWithContentViewController:duplicateDayContent];
duplicateDayPopover.popoverContentSize = CGSizeMake(320, 600);
}
CGRect rect = CGRectMake(cell.bounds.origin.x+800, cell.bounds.origin.y+10, 50, 30);
[duplicateDayPopover presentPopoverFromRect:rect inView:cell permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
}

This works quite nicely for a cell with accessoryType .detailDisclosureButton:
if let ppc = vc.popoverPresentationController, let cell = tableView.cellForRow(at: indexPath) {
ppc.sourceView = cell
ppc.sourceRect = CGRect(x: cell.bounds.width - 58, y: cell.bounds.height/2 - 11, width: 22, height: 22)
vc.modalPresentationStyle = .popover
}
present(vc, animated: true, completion: nil)
You would typically do this in tableView(_ accessoryButtonTappedForRowWith indexPath:)
You can also test the position of the calculated frame with a marker:
let marker = UIView(frame: ppc.sourceRect)
marker.backgroundColor = UIColor.red.withAlphaComponent(0.2)
cell.addSubview(marker)

this code from this thread helped me: How to correctly present a popover from a UITableViewCell with UIPopoverArrowDirectionRight or UIPopoverArrowDirectionLeft
thanks to rachels hint
UIView *accessoryView = cell.accessoryView; // finds custom accesoryView (cell.accesoryView)
if (accessoryView == nil) {
UIView *cellContentView = nil;
for (UIView *accView in [cell subviews]) {
if ([accView isKindOfClass:[UIButton class]]) {
accessoryView = accView; // find generated accesoryView (UIButton)
break;
} else if ([accView isKindOfClass:NSClassFromString(#"UITableViewCellContentView")]) {
// find generated UITableViewCellContentView
cellContentView = accView;
}
}
// if the UIButton doesn't exists, find cell contet view (UITableViewCellContentView)
if (accessoryView == nil) {
accessoryView = cellContentView;
}
// if the cell contet view doesn't exists, use cell view
if (accessoryView == nil) {
accessoryView = cell;
}
}

In swift, this has worked for me:
Create a popover presentation segue, then use prepareForSegue to configure the UIPopoverPresentationController of the destination view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == Storyboard.providerInfoSegue) {
if let vc = segue.destinationViewController.contentViewController as? /*DestinationViewControllerType*/ {
//Configure view controllers here
if let popOverPresentationController : UIPopoverPresentationController = vc.popoverPresentationController {
if let cell = tableView.cellForRowAtIndexPath(selectedAccessoryIndexPath) {
var accessoryView: UIButton!
for accView in cell.subviews {
if accView is UIButton {
accessoryView = accView as? UIButton
break
}
}
popOverPresentationController.delegate = self
popOverPresentationController.sourceView = cell
popOverPresentationController.sourceRect = accessoryView.frame
popOverPresentationController.permittedArrowDirections = UIPopoverArrowDirection.Right
}
}
}
}
}

Related

Custom UITableViewRowAction button

I want to set top and bottom constraint for uitableviewrowaction button
Here's my code
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewRowAction *deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:#"Delete" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
}];
deleteAction.backgroundColor = [UIColor redColor];
return #[deleteAction];
}
Like this I've added delete button. In tableviewCell I've added one UIView it has top and bottom constraints. I want the delete button to match with my view in UITableviewCell.
you can set delete button frame in your custom uitableviewcell class
like this
-(void)didTransitionToState:(UITableViewCellStateMask)state
{
[super didTransitionToState:state];
if ((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableViewCellStateShowingDeleteConfirmationMask)
{
UIView *deleteButton = [self deleteButtonSubview:self];
if (deleteButton)
{
CGRect frame = deleteButton.frame;
frame.origin.y = 4;
frame.size.height = frame.size.height-8;
/*
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
frame.size.height = 62; //vikram singh 2/1/2015
frame.size.width = 80;
}
else
{
frame.size.height = 52; //vikram singh 2/1/2015
frame.size.width = 80;
}
*/
deleteButton.frame = frame;
}
}
}
- (UIView *)deleteButtonSubview:(UIView *)view
{
if ([NSStringFromClass([view class]) rangeOfString:#"Delete"].location != NSNotFound) {
return view;
}
for (UIView *subview in view.subviews) {
UIView *deleteButton = [self deleteButtonSubview:subview];
[deleteButton setBackgroundColor:[UIColor whiteColor]];
if (deleteButton) {
return deleteButton;
}
}
return nil;
}
use didTransitionToState methods :)
A little more about Balkaran's answer... The delete button is a custom private class, but using the String(describing:) method, you can be reasonably sure you can get ahold of it.
Also, I was surprised to find that didTransition fires as soon as you start changing the state, not when the state is done changing.
An updated version of #balkaran's code in Swift 3:
override func didTransition(to state: UITableViewCellStateMask) {
super.willTransition(to: state)
if state == .showingDeleteConfirmationMask {
let deleteButton: UIView? = subviews.first(where: { (aView) -> Bool in
return String(describing: aView).contains("Delete")
})
if deleteButton != nil {
deleteButton?.frame.size.height = 50.0
}
super.willTransitionToState(state)
if state == .ShowingDeleteConfirmationMask
{
let deleteButton: UIView? = subviews[0]
if deleteButton != nil {
var frame: CGRect? = deleteButton?.frame
frame?.origin.y = 9
frame?.origin.x = 10
frame?.size.height = (frame?.size.height)! - 14
deleteButton?.frame = frame!
}
}

Control swipe back action in UINavigationController

I'm creating a simple flash card app as illustrated below:
I want a swipe backwards to occur like this:
To do this, onBack(index: Int) is what I need to be called when the swipe back happens (in order to update the card shown):
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var flashCardLabel: UILabel!
// Populate initial content
let content = ["Lorem", "Ipsum", "Dolor", "Sit"]
// Index of where we are in content
var index = 0
override func viewDidLoad() {
super.viewDidLoad()
}
// Label text based on index
func setLabelToIndex() {
flashCardLabel.text = content[index]
}
// Go back
#IBAction func back(_ sender: Any) {
if index > 0 {
index = index - 1
setLabelToIndex()
}
}
// Go forward
#IBAction func next(_ sender: Any) {
if index + 1 < content.count {
index = index + 1
setLabelToIndex()
}
}
// Desired function to be called
// when swiping back in navigation stack
func onBack(index: Int) {
self.index = index
setLabelToIndex()
}
}
If I understand your question correctly, you want to be able to swipe between questions and/or have a swipe effect when you click "Next" or "Back". If that's the case, I suggest you embed your UILabel in a UIScrollView. Check this out:
class ViewController: UIViewController,UIScrollViewDelegate {
let content = ["Lorem", "Ipsum", "Dolor", "Sit"]
let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 320, height: 300))
var index = 0
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
scrollView.contentSize = CGSize(width: self.view.frame.width * content.count, height: self.scrollView.frame.size.height)
scrollView.isPagingEnabled = true
// add labels to pages
for i in 0 ..< content.count {
let label = UILabel(frame: CGRect(x: self.view.center.x * (i + 1), y: self.view.center.y, width: 100, height: 50))
label.textAlignment = .center
label.text = content[i]
scrollView.addSubview(label)
}
self.view.addSubview(scrollView)
}
// Go back
#IBAction func back(_ sender: Any) {
if index > 0 {
index = index - 1
// scroll to page
let offset = CGPoint(x: CGFloat(index) * self.view.frame.width, y: 0)
self.scrollView.setContentOffset(offset, animated: true)
}
}
// Go forward
#IBAction func next(_ sender: Any) {
if index + 1 < content.count {
index = index + 1
// scroll to page
let offset = CGPoint(x: CGFloat(index) * self.view.frame.width, y: 0)
self.scrollView.setContentOffset(offset, animated: true)
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
index = round(scrollView.contentOffset.x / scrollView.frame.size.width)
}
}
Explanation:
You basically create a UIScrollView to handle the pagination effect and add a UILabel to each page with the respective text from the content array. Every time the user scrolls to a different page, index gets updated to the index of the current page. And finally, when the user clicks "Next" or "Back", you scroll over to the next or previous page
If you want it to by Push & Pop navigationcontroller, you can do it by making static index variable.
Here is code
class ViewController: UIViewController {
#IBOutlet weak var flashCardLabel: UILabel!
// Populate initial content
let content = ["Lorem", "Ipsum", "Dolor", "Sit"]
// Index of where we are in content
static var INDEX = 0;
override func viewDidLoad() {
super.viewDidLoad()
self.next(nil);
}
// Label text based on index
func setLabelToIndex() {
flashCardLabel.text = content[ViewController.INDEX]
}
// Go back
#IBAction func back(_ sender: Any?) {
if ViewController.INDEX > 0 {
ViewController.INDEX = ViewController.INDEX - 1
setLabelToIndex()
}
}
// Go forward
#IBAction func next(_ sender: Any?) {
if ViewController.INDEX + 1 < content.count {
ViewController.INDEX = ViewController.INDEX + 1
setLabelToIndex()
}
}
// Desired function to be called
// when swiping back in navigation stack
func onBack(index: Int) {
ViewController.INDEX -= 1;
//setLabelToIndex()
}
override func didMove(toParentViewController parent: UIViewController?) {
if parent == nil {
self.onBack(index: ViewController.INDEX);
}
}
}
i can tell you that swipe that UINavigationController suppport is the the swipe when user start swipping his finger from the left of the screen to right just to pop the view from navigation you can not push it back by swipping from right edge to left in iPhone, this is default in UINavigationController
i am writing my code as i am using you need to customize it accordinly, i didn't had time in office to edit, i will tell you more
#pragma mark for pageView
- (UIViewController *) viewControllerAtIndex:(NSUInteger)index
{
if (index > (self.imageArray.count-1))
return nil;
UIViewController *viewController = nil; ////
GalleryItems *item = self.imageArray[index];
NSString *cachedGalleryItemName = [item getCachedPhotoFileNameWithPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:cachedGalleryItemName])
{
ImageViewController *imageVC = [[ImageViewController alloc] initWithNibName:#"ImageViewController" bundle:nil];
imageVC.galleryItem = item;
imageVC.cachedGalleryItemName = cachedGalleryItemName;
imageVC.index = index;
viewController = imageVC;
}
else
{
if (self.downloadViewController)
{
if (self.indexOfDownloadInProgress == index)
viewController = self.downloadViewController;
else
{
FileDownloader *fileDownloader = [DataDownloadManager existingFileDownloader:cachedGalleryItemName];
if (! fileDownloader)
{
fileDownloader = [[FileDownloader alloc] init];
[fileDownloader loadURL:item.photoURL forFilePath:cachedGalleryItemName withReceipt:nil];
fileDownloader.delegate = nil;
fileDownloader.notificationName = item.contentId;
fileDownloader.queuePriority = NSOperationQueuePriorityNormal;
[[DataDownloadManager sharedInstance].operationQueue addOperation:fileDownloader];
}
}
}
else
{
DownloadViewController *downloadVC = [[DownloadViewController alloc] initWithNibName:#"DownloadViewController" bundle:nil];
downloadVC.delegate = self;
downloadVC.downloadCompleteNotificationName = item.contentId;
downloadVC.asset = item;
downloadVC.backgroundImageFileName = nil;
downloadVC.totalFileSize = nil;
downloadVC.URLString = item.photoURL;
downloadVC.cachedFileName = cachedGalleryItemName;
self.indexOfDownloadInProgress = index;
self.downloadViewController = downloadVC;
viewController = downloadVC;
}
}
return viewController;
}
Now use this function to identify the view controller
-(NSUInteger) indexOfViewController:(UIViewController *)viewController
{
NSUInteger index = nil;
if ([viewController isMemberOfClass:[ImageViewController class]])
{
ImageViewController *currentViewController = (ImageViewController *)viewController;
index = currentViewController.index;
}
else if ([viewController isMemberOfClass:[DownloadViewController class]])
index = self.indexOfDownloadInProgress;
return index;
}
- (UIViewController *)viewController:(UIViewController *)viewController ForBeforeAfter:(NSInteger) beforeAfter
{
NSUInteger index = [self indexOfViewController:viewController];
if (index == NSNotFound)
return nil;
index = index + beforeAfter;
if ([DataDownloadManager sharedInstance].internetNotAvailable)
{
while (index < self.imageArray.count - 1)
{
GalleryItems *item = self.imageArray[index];
if ([item isDownloaded])
break;
index = index + beforeAfter;
}
}
return [self viewControllerAtIndex:index];
}
now do this in page view controller delegate
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
return [self viewController:viewController ForBeforeAfter:-1];
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
return [self viewController:viewController ForBeforeAfter:+1];
}
init page view controller like this
- (void)initPageViewController:(UIViewController *)initViewController
{
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
[self.pageViewController setDataSource:self];
[self.pageViewController setViewControllers:#[initViewController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
[self.pageViewController.view setFrame:self.view.frame];
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
[self.view sendSubviewToBack:self.pageViewController.view];
}
in viewDidLoad of the class(in my case it is DisplayImageViewController) you are using this page you can add this tine of code for initialization
[self initPageViewController:[self viewControllerAtIndex:self.index]];
this DisplayImageViewController class is used to display the image you just remove the UIIMAGE to something you want.
and before you push this view controller in navigation set the property like this
DisplayImageViewController *divc = initialize display view controller class; // here you just set the item in array in which you want to implement swipe
divc.imageArray = self.imageArray;
divc.galleryAsset = self.gallery;
divc.index = indexPath.item;
[self presentViewController:divc animated:YES completion:nil];

report an error Ambiguous reference to member 'subviews'

Changes in the table a multiple-choice button style
NSArray *subviews = [[tableView cellForRowAtIndexPath:indexPath] subviews];/* An array of */
for (id subCell in subviews) {
if ([subCell isKindOfClass:[UIControl class]]) {
for (UIImageView *circleImage in [subCell subviews]) {
circleImage.image = [UIImage imageNamed:#"CellButtonSelected"];
}
}
}
/* change */
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) /* table Proxy method */
var subviews = tableView.cellForRowAtIndexPath(indexPath)!.subviews
for subCell: AnyObject in subviews {
if (subCell is UIControl) {
for circleImage: UIImageView in subCell.subviews { /* report an error Ambiguous reference to member 'subviews' */
/* How to solve?*/
circleImage.image = UIImage(named: "CellButtonSelected")!
}
}
}
report an error Ambiguous reference to member 'subviews'
/**Swift, the table of custom boxes, no system, how to change/
Maybe something like this:
for circleImage: UIView in subCell.subviews! {
if let circleImage = circleImage as? UIImageView {
circleImage.image = UIImage(named: "CellButtonSelected")!
}
}
The button click event custom selection button collapsed
for row in 0...self.collectionBookList.count {
let indexPath=NSIndexPath.init(forRow: row, inSection: 0)
self.myTableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition:.Bottom)
let subviews = self.myTableView.cellForRowAtIndexPath(indexPath)!.subviews
for subCell: AnyObject in subviews {
if (subCell is UIControl) {
for circleImage: UIView in subCell.subviews! {
if let circleImage = circleImage as? UIImageView {
circleImage.image = UIImage(named: "delete")!
}
}
}
}
}

How to get the indexpath.row when a UIImageView in a cell is tapped?

swift: how to get the indexpath.row when a button in a cell is tapped?
This link is an answer for when a button is tapped, and if my question is not possible I'll just use a button and put the image on it. I was just wondering if it is possible to this with tapping a UIImageView instead of a button. I tried the exact answer with a UIImageView instead of a UIButton and I got this error
"fatal error: unexpectedly found nil while unwrapping an Optional value".
cell.imagePosted is a UIImageView
let tapGR = UITapGestureRecognizer(target: self,action:Selector("imageTapped:"))
cell.imagePosted.userInteractionEnabled = true
cell.imagePosted.addGestureRecognizer(tapGR);
func imageTapped(img: AnyObject)
{
if let imgView = img as? UIImageView {
if let superView = imgView.superview {
if let cell = superView.superview as? CellCustom {
indexPath2 = self.tableView.indexPathForCell(cell)
}
}
}
print(indexPath2.row)
}
this might help you,
add an UITapGestureRecognizer to UIImageView
You can store indexpath.row in tag property of UIImageView and access that tag on UITapGestureRecognizer event
for example (Objective-C) :
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleImageTap:)];
tap.cancelsTouchesInView = YES;
tap.numberOfTapsRequired = 1;
[cell.imageView addGestureRecognizer:tap];
cell.imageView.tag = indexPath.row;
and get indexpath.row
-(void)handleImageTap:(UITapGestureRecognizer *)gestureRecognizer{
UIView* view = gestureRecognizer.view;
CGPoint loc = [gestureRecognizer locationInView:view];
NSInteger indexpath = [view hitTest:loc withEvent:nil].tag;
NSLog(#"%ld",(long)indexpath);
}
You can create a protocol with a method like imageViewInCellTapped(cell:YourCellType)and a delegate property in the cell. In cellForRowAtIndexPath set the controller the delegate of each cell and implement the method from the protocol. When something happens in your cell like a button or image is tapped you can call delegate?.imageViewInCellTapped(self) where self is the cell then in your controller where this method is implemented you get the index using indexPathForCell method of the tableView.
Here is Madan's code in swift if anybody is interested:
func imageTapped(gestureRecognizer: UITapGestureRecognizer) {
var view: UIView!
var loc: CGPoint!
view = gestureRecognizer.view
loc = gestureRecognizer.locationInView(view)
var indexPath: NSInteger!
indexPath = (view.hitTest(loc, withEvent: nil)?.tag)!
print(indexPath)
}
Hope this code helps to get indexpath for tapped cell swift 3:
func nameTapped(_ sender:UITapGestureRecognizer)
{
var pointVAlue = CGPoint()
pointVAlue = sender.location(in: infoTable)
var indexPath = IndexPath()
indexPath = infoTable.indexPathForRow(at: pointVAlue)!
print(indexPath.row)
/* if indexPath != nil {
let userEnt = ratingArray.object(at: indexPath.row)as! RateEntity
let detailVc = AssociateDetailViewController()
if ((userEnt.userId as String).isEmpty == false )
{
detailVc.m_otherUserId = userEnt.userId as String
self.navController!.pushViewController(detailVc, animated: true)
}
}*/
}

How do I make a UITableViewCell appear disabled?

I know about UITableview: How to Disable Selection for Some Rows but Not Others and cell.selectionStyle = UITableViewCellSelectionStyleNone, but how do I make a cell (or any UIView for that matter) appear disabled (grayed-out) like below?
You can just disable the cell's text fields to gray them out:
Swift 4.x
cell!.isUserInteractionEnabled = false
cell!.textLabel!.isEnabled = false
cell!.detailTextLabel!.isEnabled = false
A Swift extension that works well in the context I'm using it; your mileage may vary.
Swift 2.x
extension UITableViewCell {
func enable(on: Bool) {
for view in contentView.subviews as! [UIView] {
view.userInteractionEnabled = on
view.alpha = on ? 1 : 0.5
}
}
}
Swift 3:
extension UITableViewCell {
func enable(on: Bool) {
for view in contentView.subviews {
view.isUserInteractionEnabled = on
view.alpha = on ? 1 : 0.5
}
}
}
Now it's just a matter of calling myCell.enable(truthValue).
Thanks to #Ajay Sharma, I figured out how to make a UITableViewCell appear disabled:
// Mac's native DigitalColor Meter reads exactly {R:143, G:143, B:143}.
cell.textLabel.alpha = 0.439216f; // (1 - alpha) * 255 = 143
aSwitch.enabled = NO; // or [(UISwitch *)cell.accessoryView setEnabled:NO];
And then, to actually disable the cell:
cell.userInteractionEnabled = NO;
Try using a small trick:
Just set the alpha of the cell. Put some condition as your own requirements & set the alpha.
cell.alpha=0.2;
If it does't work,the way you like it to be then, Use second trick,
Just take an image of the cell size having gray background with Transparent Background, just add that image in image over the cell content.
Like this:
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
if(indexPath.row==0)
{
cell.userInteractionEnabled=FALSE;
UIImageView *img=[[UIImageView alloc]init];
img.frame=CGRectMake(0, 0, 320, 70);
img.image=[UIImage imageNamed:#"DisableImage.png"];
img.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:img];
[img release];
}
else {
//Your usual code for cell interaction.
}
return cell;
}
Although I am not not sure about the way,but this will surely fulfill your requirement.This will give a kind of illusion in user's mind that the cell is Disable.
Just try using this solution.Hope that will solve your problem.
Swift 4.X
Nice Extension from Kevin Owens,
I am correcting the behaviour of cell.
extension UITableViewCell {
func enable(on: Bool) {
self.isUserInteractionEnabled = on
for view in contentView.subviews {
self.isUserInteractionEnabled = on
view.alpha = on ? 1 : 0.5
}
}
}
How to call this:-
cell.enable(on: switch.isOn)
Great extension from Kevin Owens, this is my correction to working with Swift 2.x:
extension UITableViewCell {
func enable(on: Bool) {
self.userInteractionEnabled = on
for view in contentView.subviews {
view.userInteractionEnabled = on
view.alpha = on ? 1 : 0.5
}
}
}
Swift 3:
extension UITableViewCell {
func enable(on: Bool) {
self.isUserInteractionEnabled = on
for view in contentView.subviews {
view.isUserInteractionEnabled = on
view.alpha = on ? 1 : 0.5
}
}
}
I have created following extension to Enable/Disable UITableViewCell, it is very convenient to use it.
Create UITableViewCell Extension with "UITableViewCell+Ext.h" contain following in it.
#interface UITableViewCell (Ext)
- (void)enableCell:(BOOL)enabled withText:(BOOL)text;
- (void)enableCell:(BOOL)enabled withText:(BOOL)text withDisclosureIndicator:(BOOL)disclosureIndicator;
- (void)disclosureIndicator:(BOOL)disclosureIndicator;
#end
"UITableViewCell+Ext.m" contain following in it.
#implementation UITableViewCell (Ext)
- (UITableView *)uiTableView {
if ([[UIDevice currentDevice] systemVersionIsGreaterThanOrEqualTo:#"7.0"]) {
return (UITableView *)self.superview.superview;
}
else {
return (UITableView *)self.superview;
}
}
- (void)enableCell:(BOOL)enabled withText:(BOOL)text {
if (enabled) {
self.userInteractionEnabled = YES;
if (text) {
self.textLabel.alpha = 1.0f;
self.alpha = 1.0f;
self.detailTextLabel.hidden = NO;
}
}
else {
self.userInteractionEnabled = NO;
if (text) {
self.textLabel.alpha = 0.5f;
self.alpha = 0.5f;
self.detailTextLabel.hidden = YES;
}
}
}
- (void)enableCell:(BOOL)enabled withText:(BOOL)text withDisclosureIndicator:(BOOL)disclosureIndicator {
if (enabled) {
self.userInteractionEnabled = YES;
if (text) {
self.textLabel.alpha = 1.0f;
self.alpha = 1.0f;
self.detailTextLabel.hidden = NO;
}
self.accessoryType = disclosureIndicator ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
}
else {
self.userInteractionEnabled = NO;
if (text) {
self.textLabel.alpha = 0.5f;
self.alpha = 0.5f;
self.detailTextLabel.hidden = YES;
}
self.accessoryType = UITableViewCellAccessoryNone;
}
}
- (void)disclosureIndicator:(BOOL)disclosureIndicator {
if (disclosureIndicator) {
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else {
self.accessoryType = UITableViewCellAccessoryNone;
}
}
#end
How to Disable Cell:
[cell enableCell:NO withText:NO];
[cell enableCell:NO withText:YES withDisclosureIndicator:YES];
How to Enable Cell:
[cell enableCell:YES withText:NO];
[cell enableCell:YES withText:YES withDisclosureIndicator:YES];
Hope it helps you.
for swift
cell.isUserInteractionEnabled = false
Swift 5 version
class CustomCell: UITableViewCell {
private func isEnabled(_ enabled: Bool) {
isUserInteractionEnabled = enabled
subviews.forEach { subview in
subview.isUserInteractionEnabled = enabled
subview.alpha = enabled ? 1 : 0.5
}
}
}

Resources