Custom UITableViewRowAction button - ios

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!
}
}

Related

Unable to load images asynchronous with FSPagerView

I have problem displaying the images in the slider because I have to wait for them to download and then load them inside the FSPagerViewCell ImageView.
Inside the delegate function
- (FSPagerViewCell *)pagerView:(FSPagerView *)pagerView cellForItemAtIndex:(NSInteger)index
I am using an async function which when the image is ready, returns the downloaded image.
I use
dispatch_async(dispatch_get_main_queue(), ^{
to load the image inside the FSPagerViewCell.imageView
but it doesn't show anything, just white space.
Here is the complete code:
- (FSPagerViewCell *)pagerView:(FSPagerView *)pagerView cellForItemAtIndex:(NSInteger)index
{
FSPagerViewCell *cell = [pagerView dequeueReusableCellWithReuseIdentifier:#"pagerCell" atIndex:index];
if (index < [arrMediaGallery count])
{
pageControl.currentPage=index;
MediaGallery *firstImage = arrMediaGallery[index];
NSString *imageName = firstImage.Source;
NSString *imageURL;
if (imageName != nil && [imageName isEqualToString:#"NULL"] == NO && [imageName isEqualToString:#""] == NO) {
imageName = [NSString stringWithFormat:#"thumblarge_%#", imageName];
imageURL = [NSString stringWithFormat:#"%#/cache/photos/%#", [MyApp sharedInstance].settings.StaticURL, imageName];
}
UIImage *image = [NetworkUtilities resolveImageWithName:imageName];
if (image == nil) {
ProcessServerData *processData = [ProcessServerData new];
[processData downloadImageForURL:imageURL verifyDownload:NO
verifyHash:nil
oldImageName:nil
parentView:[cell contentView]
completion:^(NSError *error) {
UIImage* image = [NetworkUtilities resolveImageWithName:[NSString stringWithFormat:#"%#", imageName]];
dispatch_async(dispatch_get_main_queue(), ^{
[UIView transitionWithView:cell.imageView
duration:2.0f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[cell.imageView setImage:image];
} completion:nil];
});
}];
}
else {
[cell.imageView setImage:image];
}
}
return cell;
}
Am I doing something wrong?
Step:1 - install From "https://github.com/WenchaoD/FSPagerView"
Step:2 - import "FSPagerViewDataSource,FSPagerViewDelegate" in VC
Step:3 - implement methods
//MARK: - FSPagerview Delegate and Datasource -
func numberOfItems(in pagerView: FSPagerView) -> Int
{
if pagerView == self.News_images_View
{
return 3
}
else
{
return 3
}
}
func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell
{
let cell = pagerView.dequeueReusableCell(withReuseIdentifier: "cell", at: index)
cell.imageView?.contentMode = .scaleAspectFit
cell.imageView?.clipsToBounds = true
if pagerView == self.News_images_View
{
cell.imageView?.image = UIImage(named: "noticia.jpg")
}
return cell
}
func pagerView(_ pagerView: FSPagerView, didSelectItemAt index: Int)
{
if pagerView == self.News_images_View
{
}
}
#IBOutlet var viewFirstAdvView: FSPagerView!
Inside ViewDidLoad
self.viewFirstAdvView.register(FSPagerViewCell.self, forCellWithReuseIdentifier: "cell") self.viewFirstAdvView.itemSize =
CGSize(width: self.view.frame.size.width, height:
UIScreen.main.bounds.size.width / 2)
self.viewFirstAdvView.isInfinite = true
self.viewFirstAdvView.automaticSlidingInterval = 3.0
self.lastAdvertiseView.transformer =
FSPagerViewTransformer(type:.crossFading)

How to disable delaysContentTouches in UITableViewController? [duplicate]

I've looked at a ton of posts on similar things, but none of them quite match or fix this issue. Since iOS 7, whenever I add a UIButton to a UITableViewCell or even to the footerview it works "fine", meaning it receives the target action, but it doesn't show the little highlight that normally happens as you tap a UIButton. It makes the UI look funky not showing the button react to touch.
I'm pretty sure this counts as a bug in iOS7, but has anyone found a solution or could help me find one :)
Edit:
I forgot to mention that it will highlight if I long hold on the button, but not a quick tap like it does if just added to a standard view.
Code:
Creating the button:
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.titleLabel.font = [UIFont systemFontOfSize:14];
button.titleLabel.textColor = [UIColor blueColor];
[button setTitle:#"Testing" forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents: UIControlEventTouchDown];
button.frame = CGRectMake(0, 0, self.view.frame.size.width/2, 40);
Things I've Tested:
//Removing gesture recognizers on UITableView in case they were getting in the way.
for (UIGestureRecognizer *recognizer in self.tableView.gestureRecognizers) {
recognizer.enabled = NO;
}
//Removing gestures from the Cell
for (UIGestureRecognizer *recognizer in self.contentView.gestureRecognizers) {
recognizer.enabled = NO;
}
//This shows the little light touch, but this isn't the desired look
button.showsTouchWhenHighlighted = YES;
In that tableview you just add this property.
tableview.delaysContentTouches = NO;
And add in cellForRowAtIndexPath after you initiate the cell you just add below code. The structure of the cell is apparently different in iOS 6 and iOS 7.
iOS 7 we have one control UITableViewCellScrollView In between UITableViewCell and content View.
for (id obj in cell.subviews)
{
if ([NSStringFromClass([obj class]) isEqualToString:#"UITableViewCellScrollView"])
{
UIScrollView *scroll = (UIScrollView *) obj;
scroll.delaysContentTouches = NO;
break;
}
}
Since iOS 8 we need to apply the same technique to UITableView subviews (table contains a hidden UITableViewWrapperView scroll view). There is no need iterate UITableViewCell subviews anymore.
for (UIView *currentView in tableView.subviews) {
if ([currentView isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)currentView).delaysContentTouches = NO;
break;
}
}
This answer should be linked with this question.
I tried to add this to the accepted answer but it never went through. This is a much safer way of turning off the cells delaysContentTouches property as it does not look for a specific class, but rather anything that responds to the selector.
In Cell:
for (id obj in self.subviews) {
if ([obj respondsToSelector:#selector(setDelaysContentTouches:)]) {
[obj setDelaysContentTouches:NO];
}
}
In TableView:
self.tableView.delaysContentTouches = NO;
For a solution that works in both iOS7 and iOS8, create a custom UITableView subclass and custom UITableViewCell subclass.
Use this sample UITableView's initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// iterate over all the UITableView's subviews
for (id view in self.subviews)
{
// looking for a UITableViewWrapperView
if ([NSStringFromClass([view class]) isEqualToString:#"UITableViewWrapperView"])
{
// this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
Use this sample UITableViewCell's initWithStyle:reuseIdentifier:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// iterate over all the UITableViewCell's subviews
for (id view in self.subviews)
{
// looking for a UITableViewCellScrollView
if ([NSStringFromClass([view class]) isEqualToString:#"UITableViewCellScrollView"])
{
// this test is here for safety only, also there is no UITableViewCellScrollView in iOS8
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
What I did to solve the problem was a category of UIButton using the following code :
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
[NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = YES; }];
}
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self performSelector:#selector(setDefault) withObject:nil afterDelay:0.1];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self performSelector:#selector(setDefault) withObject:nil afterDelay:0.1];
}
- (void)setDefault
{
[NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = NO; }];
}
the button reacts correctly when I press on it in a UITableViewCell, and my UITableView behaves normally as the delaysContentTouches isn't forced.
Here's Roman B's answer in Swift 2:
for view in tableView.subviews {
if view is UIScrollView {
(view as? UIScrollView)!.delaysContentTouches = false
break
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
for (id view in self.tableView.subviews)
{
// looking for a UITableViewWrapperView
if ([NSStringFromClass([view class]) isEqualToString:#"UITableViewWrapperView"])
{
// this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
I was having similar issues with a text-only UIButton in a UITableViewCell not highlighting upon touch. What fixed it for me was changing the buttonType from Custom back to System.
Setting delaysContentTouches to NO did the trick for the image-only UIButton in the same UITableViewCell.
self.tableView.delaysContentTouches = NO;
This is a Swift version of Raphaƫl Pinto's answer above. Don't forget to upvote him too :)
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = true }
}
override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
super.touchesCancelled(touches, withEvent: event)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
self.setDefault()
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
self.setDefault()
}
}
func setDefault() {
NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = false }
}
Solution in Swift, iOS8 only (needs the extra work on each of the cells for iOS7):
//
// NoDelayTableView.swift
// DivineBiblePhone
//
// Created by Chris Hulbert on 30/03/2015.
// Copyright (c) 2015 Chris Hulbert. All rights reserved.
//
// This solves the delayed-tap issue on buttons on cells.
import UIKit
class NoDelayTableView: UITableView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
delaysContentTouches = false
// This solves the iOS8 delayed-tap issue.
// http://stackoverflow.com/questions/19256996/uibutton-not-showing-highlight-on-tap-in-ios7
for view in subviews {
if let scroll = view as? UIScrollView {
scroll.delaysContentTouches = false
}
}
}
override func touchesShouldCancelInContentView(view: UIView!) -> Bool {
// So that if you tap and drag, it cancels the tap.
return true
}
}
To use, all you have to do is change the class to NoDelayTableView in your storyboard.
I can confirm that in iOS8, buttons placed inside a contentView in a cell now highlight instantly.
Slightly modified version of Chris Harrison's answer. Swift 2.3:
class HighlightButton: UIButton {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = true }
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
super.touchesCancelled(touches, withEvent: event)
setDefault()
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
setDefault()
}
private func setDefault() {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = false }
}
}
}
The accepted answer did not work at some "taps" for me .
Finally I add the bellow code in a uibutton category(/subclass),and it works a hundred percent.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.backgroundColor = [UIColor greenColor];
[UIView animateWithDuration:0.05 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
self.backgroundColor = [UIColor clearColor];
} completion:^(BOOL finished)
{
}];
[super touchesBegan:touches withEvent:event];
}
I wrote a category extension on UITableViewCell to make this issue simple to address. It does basically the same thing as the accepted answer except I walk up the view hierarchy (as opposed to down) from the UITableViewCell contentView.
I considered a fully "automagic" solution that would make all cells added to a UITableView set their delaysContentTouches state to match the owning UITableView's delaysContentTouches state. To make this work I'd have to either swizzle UITableView, or require the developer to use a UITableView subclass. Not wanting to require either I settled on this solution which I feel is simpler and more flexible.
Category extension and sample harness here:
https://github.com/TomSwift/UITableViewCell-TS_delaysContentTouches
It's dead-simple to use:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// using static cells from storyboard...
UITableViewCell* cell = [super tableView: tableView cellForRowAtIndexPath: indexPath];
cell.ts_delaysContentTouches = NO;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
Here's the code for the category:
#interface UITableViewCell (TS_delaysContentTouches)
#property (nonatomic, assign) BOOL ts_delaysContentTouches;
#end
#implementation UITableViewCell (TS_delaysContentTouches)
- (UIScrollView*) ts_scrollView
{
id sv = self.contentView.superview;
while ( ![sv isKindOfClass: [UIScrollView class]] && sv != self )
{
sv = [sv superview];
}
return sv == self ? nil : sv;
}
- (void) setTs_delaysContentTouches:(BOOL)delaysContentTouches
{
[self willChangeValueForKey: #"ts_delaysContentTouches"];
[[self ts_scrollView] setDelaysContentTouches: delaysContentTouches];
[self didChangeValueForKey: #"ts_delaysContentTouches"];
}
- (BOOL) ts_delaysContentTouches
{
return [[self ts_scrollView] delaysContentTouches];
}
#end
Since objc is dynamic, and scrollView is the only class that responds to delaysContentTouches, this should work for both ios 7 and 8 (put it somewhere early in your tableViewController, like awakeFromNib):
for (id view in self.tableView.subviews)
{
if ([view respondsToSelector:#selector(delaysContentTouches)]) {
UIScrollView *scrollView = (UIScrollView *)view;
scrollView.delaysContentTouches = NO;
break;
}
}
You may also have to turn off "delaysContentTouches" in your storyboard or nib by selecting the table inside your viewController. BTW, this might not work on ios 7 if you're using a tableView inside a viewController, at least I couldn't get it to work.
That solution for me doesn't work, I fixed subclassing TableView and implementing these two methods
- (instancetype)initWithCoder:(NSCoder *)coder{
self = [super initWithCoder:coder];
if (self) {
for (id obj in self.subviews) {
if ([obj respondsToSelector:#selector(setDelaysContentTouches:)]){
[obj performSelector:#selector(setDelaysContentTouches:) withObject:NO];
}
}
}
return self;
}
- (BOOL)delaysContentTouches{
return NO;
}
Solution in Swift for iOS 7 and 8:
First I wrote a utility function:
class func classNameAsString(obj: AnyObject) -> String {
return _stdlib_getDemangledTypeName(obj).componentsSeparatedByString(".").last!
}
then I subclass UITableView and implement this:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
for view in self.subviews {
if (Utility.classNameAsString(view) == "UITableViewWrapperView") {
if view.isKindOfClass(UIScrollView) {
var scroll = (view as UIScrollView)
scroll.delaysContentTouches = false
}
break
}
}
}
I also subclass UITableViewCell and implement this:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
for view in self.subviews {
if (Utility.classNameAsString(view) == "UITableViewCellScrollView") {
if view.isKindOfClass(UIScrollView) {
var scroll = (view as UIScrollView)
scroll.delaysContentTouches = false
}
}
}
}
In my case the init(coder:) will run. Please put debug point in your init functions to know which init function will run, then using the code above to make it work.
Hope to help someone.
In Swift 3 this UIView extension can be used on the UITableViewCell. Preferably in the cellForRowAt method.
func removeTouchDelayForSubviews() {
for subview in subviews {
if let scrollView = subview as? UIScrollView {
scrollView.delaysContentTouches = false
} else {
subview.removeTouchDelayForSubviews()
}
}
}

Present popover from cells accessory button?

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
}
}
}
}
}

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
}
}
}

Having a UITextField in a UITableViewCell

I'm trying to do that for a couple of days now, and after reading tons of messages of people trying to do that too, I'm still unable to have a fully working UITextField in some of my UITableViewCells, just like in this example:
Either I have the form working but the text is not visible (although I set its color to blue), the keyboard goes on the field when I click on it and I haven't been able to correctly implement the keyboard events.
I tried with a bunch of examples from Apple (mainly UICatalog, where there is a kinda similar control) but it's still not working correctly.
Can somebody help me (and all the people trying to realize this control) and post a simple implementation of a UITextField in a UITableViewCell, that works fine?
Try this out. Works like a charm for me (on iPhone devices). I used this code for a login screen once. I configured the table view to have two sections. You can of course get rid of the section conditionals.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:kCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
if ([indexPath section] == 0) {
UITextField *playerTextField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
playerTextField.adjustsFontSizeToFitWidth = YES;
playerTextField.textColor = [UIColor blackColor];
if ([indexPath row] == 0) {
playerTextField.placeholder = #"example#gmail.com";
playerTextField.keyboardType = UIKeyboardTypeEmailAddress;
playerTextField.returnKeyType = UIReturnKeyNext;
}
else {
playerTextField.placeholder = #"Required";
playerTextField.keyboardType = UIKeyboardTypeDefault;
playerTextField.returnKeyType = UIReturnKeyDone;
playerTextField.secureTextEntry = YES;
}
playerTextField.backgroundColor = [UIColor whiteColor];
playerTextField.autocorrectionType = UITextAutocorrectionTypeNo; // no auto correction support
playerTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; // no auto capitalization support
playerTextField.textAlignment = UITextAlignmentLeft;
playerTextField.tag = 0;
//playerTextField.delegate = self;
playerTextField.clearButtonMode = UITextFieldViewModeNever; // no clear 'x' button to the right
[playerTextField setEnabled: YES];
[cell.contentView addSubview:playerTextField];
[playerTextField release];
}
}
if ([indexPath section] == 0) { // Email & Password Section
if ([indexPath row] == 0) { // Email
cell.textLabel.text = #"Email";
}
else {
cell.textLabel.text = #"Password";
}
}
else { // Login button section
cell.textLabel.text = #"Log in";
}
return cell;
}
Result looks like this:
Here is a solution that looks good under iOS6/7/8/9.
Update 2016-06-10: this still works with iOS 9.3.3
Thanks for all your support, this is now on CocoaPods/Carthage/SPM at https://github.com/fulldecent/FDTextFieldTableViewCell
Basically we take the stock UITableViewCellStyleValue1 and staple a UITextField where the detailTextLabel is supposed to be. This gives us automatic placement for all scenarios: iOS6/7/8/9, iPhone/iPad, Image/No-image, Accessory/No-accessory, Portrait/Landscape, 1x/2x/3x.
Note: this is using storyboard with a UITableViewCellStyleValue1 type cell named "word".
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell = [tableView dequeueReusableCellWithIdentifier:#"word"];
cell.detailTextLabel.hidden = YES;
[[cell viewWithTag:3] removeFromSuperview];
textField = [[UITextField alloc] init];
textField.tag = 3;
textField.translatesAutoresizingMaskIntoConstraints = NO;
[cell.contentView addSubview:textField];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:cell.textLabel attribute:NSLayoutAttributeTrailing multiplier:1 constant:8]];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeTop multiplier:1 constant:8]];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeBottom multiplier:1 constant:-8]];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:cell.detailTextLabel attribute:NSLayoutAttributeTrailing multiplier:1 constant:0]];
textField.textAlignment = NSTextAlignmentRight;
textField.delegate = self;
return cell;
}
Here is how I have achieved this:
TextFormCell.h
#import <UIKit/UIKit.h>
#define CellTextFieldWidth 90.0
#define MarginBetweenControls 20.0
#interface TextFormCell : UITableViewCell {
UITextField *textField;
}
#property (nonatomic, retain) UITextField *textField;
#end
TextFormCell.m
#import "TextFormCell.h"
#implementation TextFormCell
#synthesize textField;
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
// Adding the text field
textField = [[UITextField alloc] initWithFrame:CGRectZero];
textField.clearsOnBeginEditing = NO;
textField.textAlignment = UITextAlignmentRight;
textField.returnKeyType = UIReturnKeyDone;
[self.contentView addSubview:textField];
}
return self;
}
- (void)dealloc {
[textField release];
[super dealloc];
}
#pragma mark -
#pragma mark Laying out subviews
- (void)layoutSubviews {
CGRect rect = CGRectMake(self.contentView.bounds.size.width - 5.0,
12.0,
-CellTextFieldWidth,
25.0);
[textField setFrame:rect];
CGRect rect2 = CGRectMake(MarginBetweenControls,
12.0,
self.contentView.bounds.size.width - CellTextFieldWidth - MarginBetweenControls,
25.0);
UILabel *theTextLabel = (UILabel *)[self textLabel];
[theTextLabel setFrame:rect2];
}
It may seems a bit verbose, but it works!
Don't forget to set the delegate!
This should not be difficult. When creating a cell for your table, add a UITextField object to the cell's content view
UITextField *txtField = [[UITextField alloc] initWithFrame....]
...
[cell.contentView addSubview:txtField]
Set the delegate of the UITextField as self (ie your viewcontroller) Give a tag to the text field so you can identify which textfield was edited in your delegate methods. The keyboard should pop up when the user taps the text field. I got it working like this. Hope it helps.
Try this one. It can handle scrolling as well and you can reuse the cells without the hassle of removing subviews you added before.
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section{
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [table dequeueReusableCellWithIdentifier:#"Cell"];
if( cell == nil)
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"] autorelease];
cell.textLabel.text = [[NSArray arrayWithObjects:#"First",#"Second",#"Third",#"Forth",#"Fifth",#"Sixth",#"Seventh",#"Eighth",#"Nineth",#"Tenth",nil]
objectAtIndex:indexPath.row];
if (indexPath.row % 2) {
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 200, 21)];
textField.placeholder = #"Enter Text";
textField.text = [inputTexts objectAtIndex:indexPath.row/2];
textField.tag = indexPath.row/2;
textField.delegate = self;
cell.accessoryView = textField;
[textField release];
} else
cell.accessoryView = nil;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
[inputTexts replaceObjectAtIndex:textField.tag withObject:textField.text];
return YES;
}
- (void)viewDidLoad {
inputTexts = [[NSMutableArray alloc] initWithObjects:#"",#"",#"",#"",#"",nil];
[super viewDidLoad];
}
Details
Xcode 10.2 (10E125), Swift 5
Full Sample Code
TextFieldInTableViewCell
import UIKit
protocol TextFieldInTableViewCellDelegate: class {
func textField(editingDidBeginIn cell:TextFieldInTableViewCell)
func textField(editingChangedInTextField newText: String, in cell: TextFieldInTableViewCell)
}
class TextFieldInTableViewCell: UITableViewCell {
private(set) weak var textField: UITextField?
private(set) weak var descriptionLabel: UILabel?
weak var delegate: TextFieldInTableViewCellDelegate?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupSubviews()
}
private func setupSubviews() {
let stackView = UIStackView()
stackView.distribution = .fill
stackView.alignment = .leading
stackView.spacing = 8
contentView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 6).isActive = true
stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -6).isActive = true
stackView.leftAnchor.constraint(equalTo: leftAnchor, constant: 16).isActive = true
stackView.rightAnchor.constraint(equalTo: rightAnchor, constant: -16).isActive = true
let label = UILabel()
label.text = "Label"
stackView.addArrangedSubview(label)
descriptionLabel = label
let textField = UITextField()
textField.textAlignment = .left
textField.placeholder = "enter text"
textField.setContentHuggingPriority(.fittingSizeLevel, for: .horizontal)
stackView.addArrangedSubview(textField)
textField.addTarget(self, action: #selector(textFieldValueChanged(_:)), for: .editingChanged)
textField.addTarget(self, action: #selector(editingDidBegin), for: .editingDidBegin)
self.textField = textField
stackView.layoutSubviews()
selectionStyle = .none
let gesture = UITapGestureRecognizer(target: self, action: #selector(didSelectCell))
addGestureRecognizer(gesture)
}
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
}
extension TextFieldInTableViewCell {
#objc func didSelectCell() { textField?.becomeFirstResponder() }
#objc func editingDidBegin() { delegate?.textField(editingDidBeginIn: self) }
#objc func textFieldValueChanged(_ sender: UITextField) {
if let text = sender.text { delegate?.textField(editingChangedInTextField: text, in: self) }
}
}
ViewController
import UIKit
class ViewController: UIViewController {
private weak var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
}
extension ViewController {
func setupTableView() {
let tableView = UITableView(frame: .zero)
tableView.register(TextFieldInTableViewCell.self, forCellReuseIdentifier: "TextFieldInTableViewCell")
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = UITableView.automaticDimension
tableView.tableFooterView = UIView()
self.tableView = tableView
tableView.dataSource = self
let gesture = UITapGestureRecognizer(target: tableView, action: #selector(UITextView.endEditing(_:)))
tableView.addGestureRecognizer(gesture)
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int { return 1 }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 2 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TextFieldInTableViewCell") as! TextFieldInTableViewCell
cell.delegate = self
return cell
}
}
extension ViewController: TextFieldInTableViewCellDelegate {
func textField(editingDidBeginIn cell: TextFieldInTableViewCell) {
if let indexPath = tableView?.indexPath(for: cell) {
print("textfield selected in cell at \(indexPath)")
}
}
func textField(editingChangedInTextField newText: String, in cell: TextFieldInTableViewCell) {
if let indexPath = tableView?.indexPath(for: cell) {
print("updated text in textfield in cell as \(indexPath), value = \"\(newText)\"")
}
}
}
Result
I had been avoiding this by calling a method to run [cell.contentView bringSubviewToFront:textField] every time my cells appeared, but then I discovered this relatively simple technique:
cell.accessoryView = textField;
Doesn't seem to have the same background-overpasting issue, and it aligns itself on its own (somewhat). Also, the textLabel auto-truncates to avoid overflowing into (or under) it, which is handy.
I ran into the same problem. It seems that setting the cell.textlabel.text property brings the UILabel to the front of the contentView of the cell.
Add the textView after setting textLabel.text, or (if that's not possible) call this:
[cell.contentView bringSubviewToFront:textField]
I really struggled with this task on the iPad, with text fields showing up invisible in the UITableView, and the whole row turning blue when it gets focus.
What worked for me in the end was the technique described under "The Technique for Static Row Content" in Apple's
Table View Programming Guide. I put both the label and the textField in a UITableViewCell in the NIB for the view, and pull that cell out via an outlet in cellForRowAtIndexPath:. The resulting code is much neater than UICatalog.
Here's how its done i believe the correct way. It works on Ipad and Iphone as i tested it. We have to create our own customCells by classing a uitableviewcell:
start off in interfaceBuilder ...
create a new UIViewcontroller call it customCell (volunteer for a xib while your there)
Make sure customCell is a subclass of uitableviewcell
erase all views now and create one view make it the size of a individual cell. make that view subclass customcell. now create two other views (duplicate the first).
Go to your connections inspector and find 2 IBOutlets you can connect to these views now.
-backgroundView
-SelectedBackground
connect these to the last two views you just duplicated and dont worry about them.
the very first view that extends customCell, put your label and uitextfield inside of it.
got into customCell.h and hook up your label and textfield. Set the height of this view to say 75 (height of each cell)
all done.
In your customCell.m file make sure the constructor looks something like this:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
self = [nibArray objectAtIndex:0];
}
return self;
}
Now create a UITableViewcontroller and in this method use the customCell class like this :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
// lets use our customCell which has a label and textfield already installed for us
customCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
//cell = [[[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
NSArray *topLevelsObjects = [[NSBundle mainBundle] loadNibNamed:#"NewUserCustomCell" owner:nil options:nil];
for (id currentObject in topLevelsObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (customCell *) currentObject;
break;
}
}
NSUInteger row = [indexPath row];
switch (row) {
case 0:
{
cell.titleLabel.text = #"First Name"; //label we made (uitextfield also available now)
break;
}
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 75.0;
}
Here's a drop-in subclass for UITableViewCell which replaces the detailTextLabel with an editable UITextField (or, in case of UITableViewCellStyleDefault, replaces the textLabel). This has the benefit that it allows you to re-use all the familiar UITableViewCellStyles, accessoryViews, etc, just now the detail is editable!
#interface GSBEditableTableViewCell : UITableViewCell <UITextFieldDelegate>
#property UITextField *textField;
#end
#interface GSBEditableTableViewCell ()
#property UILabel *replace;
#end
#implementation GSBEditableTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_replace = (style == UITableViewCellStyleDefault)? self.textLabel : self.detailTextLabel;
_replace.hidden = YES;
// Impersonate UILabel with an identical UITextField
_textField = UITextField.new;
[self.contentView addSubview:_textField];
_textField.translatesAutoresizingMaskIntoConstraints = NO;
[_textField.leftAnchor constraintEqualToAnchor:_replace.leftAnchor].active = YES;
[_textField.rightAnchor constraintEqualToAnchor:_replace.rightAnchor].active = YES;
[_textField.topAnchor constraintEqualToAnchor:_replace.topAnchor].active = YES;
[_textField.bottomAnchor constraintEqualToAnchor:_replace.bottomAnchor].active = YES;
_textField.font = _replace.font;
_textField.textColor = _replace.textColor;
_textField.textAlignment = _replace.textAlignment;
// Dont want to intercept UITextFieldDelegate, so use UITextFieldTextDidChangeNotification instead
[NSNotificationCenter.defaultCenter addObserver:self
selector:#selector(textDidChange:)
name:UITextFieldTextDidChangeNotification
object:_textField];
// Also need KVO because UITextFieldTextDidChangeNotification not fired when change programmatically
[_textField addObserver:self forKeyPath:#"text" options:0 context:nil];
}
return self;
}
- (void)textDidChange:(NSNotification*)notification
{
// Update (hidden) UILabel to ensure correct layout
if (_textField.text.length) {
_replace.text = _textField.text;
} else if (_textField.placeholder.length) {
_replace.text = _textField.placeholder;
} else {
_replace.text = #" "; // otherwise UILabel removed from cell (!?)
}
[self setNeedsLayout];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ((object == _textField) && [keyPath isEqualToString:#"text"]) [self textDidChange:nil];
}
- (void)dealloc
{
[_textField removeObserver:self forKeyPath:#"text"];
}
#end
Simple to use - just create your cell as before, but now use cell.textField instead of cell.detailTextLabel (or cell.textLabel in case of UITableViewCellStyleDefault). eg
GSBEditableTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (!cell) cell = [GSBEditableTableViewCell.alloc initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:#"Cell"];
cell.textLabel.text = #"Name";
cell.textField.text = _editablename;
cell.textField.delegate = self; // to pickup edits
...
Inspired by, and improved upon, FD's answer
For next/return events on multiple UITextfield inside UITableViewCell in this method I had taken UITextField in storyboard.
#interface MyViewController () {
NSInteger currentTxtRow;
}
#end
#property (strong, nonatomic) NSIndexPath *currentIndex;//Current Selected Row
#implementation MyViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CELL" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UITextField *txtDetails = (UITextField *)[cell.contentView viewWithTag:100];
txtDetails.delegate = self;
txtDetails.placeholder = self.arrReciversDetails[indexPath.row];
return cell;
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
CGPoint point = [textField convertPoint:CGPointZero toView:self.tableView];
self.currentIndex = [self.tableView indexPathForRowAtPoint:point];//Get Current UITableView row
currentTxtRow = self.currentIndex.row;
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
currentTxtRow += 1;
self.currentIndex = [NSIndexPath indexPathForRow:currentTxtRow inSection:0];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:self.currentIndex];
UITextField *currentTxtfield = (UITextField *)[cell.contentView viewWithTag:100];
if (currentTxtRow < 3) {//Currently I have 3 Cells each cell have 1 UITextfield
[currentTxtfield becomeFirstResponder];
} else {
[self.view endEditing:YES];
[currentTxtfield resignFirstResponder];
}
}
To grab the text from textfield-
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
switch (self.currentIndex.row) {
case 0:
NSLog(#"%#",[NSString stringWithFormat:#"%#%#",textField.text,string]);//Take current word and previous text from textfield
break;
case 1:
NSLog(#"%#",[NSString stringWithFormat:#"%#%#",textField.text,string]);//Take current word and previous text from textfield
break;
case 2:
NSLog(#"%#",[NSString stringWithFormat:#"%#%#",textField.text,string]);//Take current word and previous text from textfield
break;
default:
break;
}
}

Resources