Wrapping cells in UICollectionview iOS 8 - ios

I have the same problem with this question. I already tried the solution but it is not called. Where should I implement or call the method or subclass of UICollectionViewFlowLayout.
Where should I use this?
Thanks in advance.

U can do like below, this method is called automatically,
swift version
first create a new class which is the subclass of UICollectionViewFlowLayout for example
import UIKit
class CustomLayout: UICollectionViewFlowLayout
{
override init() {
super.init()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var newAttributes:[AnyObject] = []
override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
super.layoutAttributesForElementsInRect(rect)
var attributes:[AnyObject] = super.layoutAttributesForElementsInRect(rect)!
//arrayWithCapacity(attributes.count)
//configure your attributes for each item hear and store it in separate array and return that array in below example i am sending the same attributes.
return attributes
}
}
in ViewController class
import UIKit
class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource {
#IBOutlet weak var aCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var customLayout:CustomLayout = CustomLayout() //initilise the custom layout for collection view
customLayout.minimumLineSpacing = 0.33 //set the offset between items
customLayout.minimumInteritemSpacing = 0.0
customLayout.itemSize = CGSizeMake(50.0, 50.0)
aCollectionView.collectionViewLayout = customLayout //set it to collection view
var cellNib:UINib = UINib(nibName: "CollectionViewCell", bundle: nil)
aCollectionView.registerNib(cellNib, forCellWithReuseIdentifier: "CELL")
}
objective-c version
in xcode crate a new file by subclassing the UICollectionViewFlowLayout lets say it's name as MyCustomCollectionViewFlowLayout and in MyCustomCollectionViewFlowLayout .m file place the code
#import "MyCustomCollectionViewFlowLayout.h"
#implementation MyCustomCollectionViewFlowLayout
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
[super layoutAttributesForElementsInRect:rect];
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
NSMutableArray *newAttributes = [NSMutableArray arrayWithCapacity:attributes.count];
for (UICollectionViewLayoutAttributes *attribute in attributes)
{
if ((attribute.frame.origin.x + attribute.frame.size.width <= ceil(self.collectionViewContentSize.width)) &&
(attribute.frame.origin.y + attribute.frame.size.height <= ceil(self.collectionViewContentSize.height)))
{
[newAttributes addObject:attribute];
}
}
return newAttributes;
}
- (void)dealloc
{
[super dealloc];
}
#end
and the class where u are using the collection view just import MyCustomCollectionViewFlowLayout.h this and set it to collection view
- (void)viewDidLoad
{
[super viewDidLoad];
//....other codes
MyCustomCollectionViewFlowLayout *flowLayout = [[MyCustomCollectionViewFlowLayout alloc]init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
//set according to your settings
flowLayout.minimumInteritemSpacing = 0.0f;
flowLayout.minimumLineSpacing = 0.33f; //set the offset between items
_collectionView.pagingEnabled = YES;
_collectionView.bounces = NO;
_collectionView.showsHorizontalScrollIndicator = NO;
_collectionView.showsVerticalScrollIndicator = NO;
[_collectionView setCollectionViewLayout:flowLayout]; //set your custom flow layout hear
[_collectionView registerClass:[MyCollectionViewCell class] forCellWithReuseIdentifier:cellIdentifier]; //set the custom cell
}

Related

Cannot show UICollectionView with Segmented Control

In my main view Controller, I have a segmented control and a container view below. I want to change the container view whenever I switch the segmented control. When I switch over the tab, I can see the very view being loaded (print in viewDidload), however, I find it not working with the datasource of collectionViewDataSource. Any ideas?
// Main View
let videoViewController = VideoViewController()
let photoViewController = PhotoViewController()
private var activeViewController: UIViewController? {
didSet {
removeInactiveViewController(oldValue)
updateActiveViewController()
}
}
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
activeViewController = videoViewController
}
#IBAction func segmentDidChanged(sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
print("\(sender.titleForSegmentAtIndex(0)!) Selected")
activeViewController = videoViewController
} else if sender.selectedSegmentIndex == 1 {
print("\(sender.titleForSegmentAtIndex(1)!) Selected")
activeViewController = photoViewController
}
}
private func removeInactiveViewController(inactiveViewController: UIViewController?) {
if let inActiveVC = inactiveViewController {
// call before removing child view controller's view from hierarchy
inActiveVC.willMoveToParentViewController(nil)
inActiveVC.view.removeFromSuperview()
// call after removing child view controller's view from hierarchy
inActiveVC.removeFromParentViewController()
}
}
private func updateActiveViewController() {
if let activeVC = activeViewController {
// call before adding child view controller's view as subview
addChildViewController(activeVC)
activeVC.view.frame = containerView.bounds
containerView.addSubview(activeVC.view)
// call before adding child view controller's view as subview
activeVC.didMoveToParentViewController(self)
}
}
// PhotoView
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("HI")
}
extension PhotoViewController : UICollectionViewDataSource {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as!PhotoCollectionViewCell
cell.backgroundColor = UIColor.blackColor()
// Configure the cell
cell.imgView.image = UIImage(named: "test")
return cell
}
}
When I make the PhotoViewController as initial controller, it works.
I have used the UIContainerView and UISegmentControl, if you use ContainerView show single UIViewController are already posted and the link are given below,
When I add a UIContainerView to a view controller it's type is UIView. How do I get to the viewcontroller for the embedded view?
or If you use ContainerView show two UIViewController when alernatively click UISegmentControl, to show single viewcontroller code is given below,
If you create the storyboard, then go MainViewController Class and create the outlet are
#property (weak, nonatomic) IBOutlet UIView *containerView;
#property (weak, nonatomic) IBOutlet UISegmentedControl *segmentControl;
#property (weak, nonatomic) UIViewController *currentViewController;
ViewDidLoad method:
UIFont *font = [UIFont boldSystemFontOfSize:16.0f];
NSDictionary *attributes = [NSDictionary dictionaryWithObject:font
forKey:UITextAttributeFont];
[segmentControl setTitleTextAttributes:attributes
forState:UIControlStateNormal];
_currentViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstViewController"];
_currentViewController.view.layer.cornerRadius = 8.0f;
_currentViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
[self addChildViewController:_currentViewController];
[self addSubview:_currentViewController.view toView:_containerView];
And UISegmentController method is given below,
-(IBAction)indexChanged:(UISegmentedControl *)sender
{
if (sender.selectedSegmentIndex == 0) {
UIViewController *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstViewController"];
newViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
[self cycleFromViewController:self.currentViewController toViewController:newViewController];
self.currentViewController = newViewController;
}
else if (sender.selectedSegmentIndex == 1)
{
UIViewController *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
newViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
[self cycleFromViewController:self.currentViewController toViewController:newViewController];
self.currentViewController = newViewController;
}
}
And the two view related Methods are,
- (void)addSubview:(UIView *)subView toView:(UIView*)parentView {
[parentView addSubview:subView];
NSDictionary * views = #{#"subView" : subView,};
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[subView]|"
options:0
metrics:0
views:views];
[parentView addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[subView]|"
options:0
metrics:0
views:views];
[parentView addConstraints:constraints];
}
- (void)cycleFromViewController:(UIViewController*) oldViewController
toViewController:(UIViewController*) newViewController {
[oldViewController willMoveToParentViewController:nil];
[self addChildViewController:newViewController];
[self addSubview:newViewController.view toView:self.containerView];
[newViewController.view layoutIfNeeded];
// set starting state of the transition
newViewController.view.alpha = 0;
[UIView animateWithDuration:0.5
animations:^{
newViewController.view.alpha = 1;
oldViewController.view.alpha = 0;
}
completion:^(BOOL finished) {
[oldViewController.view removeFromSuperview];
[oldViewController removeFromParentViewController];
[newViewController didMoveToParentViewController:self];
}];
}
if you use these code alternativelly open the ViewController from UIContainerView when click SegmentControl, if work CollectionView from FirstViewController and its working for me!
Hope its helpful..

UISearchController - Objective C to Swift Issue

I'm trying to subclass UISearchController so I can add a custom UISearchBar. I found a way to do this in Objective-C, but I'm struggling to do it in Swift. Here are the 2 files that accomplish this in Objective-C:
CustomSearchController.h
#interface CustomSearchController : UISearchController <UISearchBarDelegate>
#end
CustomSearchController.m
#import "CustomSearchController.h"
#import "CustomSearchBar.h"
#implementation CustomSearchController
{
UISearchBar *_searchBar;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(UISearchBar *)searchBar {
if (_searchBar == nil) {
_searchBar = [[CustomSearchBar alloc] initWithFrame:CGRectZero];
_searchBar.delegate = self; // different from table search by apple where delegate was set to view controller where the UISearchController was instantiated or in our case where CustomSearchController was instantiated.
}
return _searchBar;
}
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if ([searchBar.text length] > 0) {
self.active = true;
} else {
self.active = false;
}
}
/*
Since CustomSearchController is the delegate of the search bar we must implement the UISearchBarDelegate method.
*/
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
NSLog(#"Became first responder");
[searchBar resignFirstResponder];
}
#end
The issue I'm running into is specifically with this getter:
-(UISearchBar *)searchBar {
if (_searchBar == nil) {
_searchBar = [[CustomSearchBar alloc] initWithFrame:CGRectZero];
_searchBar.delegate = self; // different from table search by apple where delegate was set to view controller where the UISearchController was instantiated or in our case where CustomSearchController was instantiated.
}
return _searchBar;
}
In Swift I believe I would be have to do something like this:
var customSearchBar: CustomSearchBar?
override var searchBar: UISearchBar {
get {
if customSearchBar == nil {
customSearchBar = CustomSearchBar()
customSearchBar?.delegate = self
}
return customSearchBar!
}
}
But is this the best way to do something like this?
Try this:
lazy var customSearchBar: CustomSearchBar = {
[unowned self] in
let result = CustomSearchBar(frame:CGRectZero)
result.delegate = self
return result
}()
override var searchBar: UISearchBar {
get {
return customSearchBar
}
}
The usage of lazy takes care of initializing the CustomSearchBar instance only when first accessed. Although I'm not sure that you really need that for what you are trying to accomplish.

UIButton not showing highlight on tap in iOS7 - Swift

From this post they said that it is kind of a bug in ios 7 and 8 - Button in UITableViewCell does not change to highlighted when tapped. Here I post one answer for this in Objective-C:
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;
}
However I cannot write in Swift. There are some problem:
1) I cannot do
self = super.init(style: style, reuseIdentifier: reuseIdentifier)
Error: Cannot assign to 'self' in a method
2) In Swift, I cannot do
view.class
as in Objective C:
[view class]
I have searched for hours but still cannot get what I want.
Please anyone can answer this in Swift?
In Swift, to call the superclass's designated initialiser, instead of calling self = [super initWithStyle:style reuseIdentifier:reuseIdentifier], you just use super.init(style: style, reuseIdentifier: reuseIdentifier)
Thanks to Schemetrical, this is the working version for me. (iOS 7 + 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
}
}
}
}

UICollectionViewCell subclass init never run

I have added a collection view in viewDidLoad like this...
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 10, 10) collectionViewLayout:flowLayout];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
self.collectionView.backgroundColor = [UIColor whiteColor];
[self.collectionView registerClass:[CameraCell class] forCellWithReuseIdentifier:CameraCellReuseIdentifier];
[self.collectionView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.collectionView];
And I have a UICollectionViewCell subclass called CameraCell with an init like this...
- (id)init
{
self = [super init];
if (self) {
// Add customisation here...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(imageChanged:) name:#"image" object:nil];
self.contentView.backgroundColor = [UIColor yellowColor];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.imageView.clipsToBounds = YES;
[self.imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.contentView addSubview:self.imageView];
NSDictionary *views = NSDictionaryOfVariableBindings(_imageView);
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_imageView]|"
options:0
metrics:nil
views:views]];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[_imageView]|"
options:0
metrics:nil
views:views]];
}
return self;
}
But when I run the app the collection view is there and I can scroll it but I can't see any cells. I have added a breakpoint in the cell's init and it never gets called. Is there another method I have to override?
EDIT
When I log the cell in cellForItemAtIndexPath...
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CameraCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CameraCellReuseIdentifier forIndexPath:indexPath];
NSLog(#"%#", cell);
return cell;
}
It displays the correct class...
<CameraCell: 0x1f07ba30; baseClass = UICollectionViewCell; frame = (0 20; 320 280); layer = <CALayer: 0x1f07bb40>>
The method you need to implement is - (instancetype)initWithFrame:(CGRect)rect
I recently tried this in the iOS7 SDK, and I had to override initWithCoder because initWithFrame was never called.
If the cell is loaded from a StoryBoard then you will want to override the initializers like this:
Swift
override init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
//You Code here
}
Objective-C
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
//You code here
}
If you are loading from a nib, then you will want to override the initializers like this:
Swift
override init(frame: CGRect) {
super.init(frame:frame)
//You Code here
}
Objective-C
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
//You code here
}
I know the question is way old, but personally I was lazy and wanted to copy-paste code from somewhere. Unfortunately, there is no 100% correct solution above so I wrote common pattern (for both IB-based and code-based cells)
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func awakeFromNib() {
commonInit()
}
private func commonInit() {
}
In my case, when I am using [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 10, 10) collectionViewLayout:flowLayout] to programatically instantiate a collectionView, initWithFrame never get called, but initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout did.
Swift 5:
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
// Your code here.
}

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