Add subview to a custom class - ios

I have a UITextField that I want to create a custom class on. So I created a file with a subclass of UITextField. Next, in the custom class, I want to implement a tableView. Kind of like a auto-complete textField.
I started creating it, and added the tableView like this:
[self addSubview:self.tableView];
When I run the app, the tableView is in the textField, so I can only see part of the tableView. How can I add it as a subview so I can see the full tableView?

This is what you are looking for
https://github.com/gaurvw/MPGTextField
This uitextfield subclass does what you want - it's builed for 'search' feature.
If you still want to use your own,
add tableview not to uitextfield itself, but like
[[self superview] addSubview:tableViewController.tableView];
EDIT:
you can set frame as:
CGRect frameForPresentation = [self frame];
frameForPresentation.origin.y += self.frame.size.height;
frameForPresentation.size.height = 200;
[tableViewController.tableView setFrame:frameForPresentation];
The way to add subview to uitextfield is to overload layoutSubviews method and init your tableview there:
- (void)layoutSubviews
{
[super layoutSubviews];
if (!self.tableview.superview)
{
[self setupView];
}
}

This will add the tableView as the subView of the textField.
self.tableView.frame = CGRectMake(0, CGRectGetHeight(self.bounds), CGRectGetWidth(self.bounds), YOUR_TABLE_HEIGHT);
[self addSubview:self.tableView];
self.clipsToBounds = NO;
However, a better way is to make the tableView as the textField's superView's subView, that is, the textField and the tableView should be siblings.

Related

How to set swipe to delete UITableview frame height in swift

The Swipe delete button shows depends on TableviewCell Height.
Need to reduce the height of the delete button.
Can anyone help me please?
It is not a good practice to change native controls, but you still can do it by subclassing UITableViewCell
#implementation UITableViewCellSubclass
- (void)layoutSubviews {
[super layoutSubviews];
if (self.showingDeleteConfirmation) {
if ([self.subviews count] < 4) return;
UIView *deleteButton = [self.subviews objectAtIndex:3];
deleteButton.frame = CGRectOffset(deleteButton.frame, 10, 10);
}
}
#end
But, it is very bad way to handle it. Better create custom UITableViewCell with custom behaviour and custom delete UIButton and then do whatever you want with it.

Dynamically resize UICollectionView's supplementary view (containing multiline UILabel's)

Inside a UICollectionView's supplementary view (header), I have a multiline label that I want to truncate to 3 lines.
When the user taps anywhere on the header (supplementary) view, I want to switch the UILabel to 0 lines so all text displays, and grow the collectionView's supplementary view's height accordingly (preferably animated). Here's what happens after you tap the header:
Here's my code so far:
// MyHeaderReusableView.m
// my gesture recognizer's action
- (IBAction)onHeaderTap:(UITapGestureRecognizer *)sender
{
self.listIntro.numberOfLines = 0;
// force -layoutSubviews to run again
[self setNeedsLayout];
[self layoutIfNeeded];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.listTitle.preferredMaxLayoutWidth = self.listTitle.frame.size.width;
self.listIntro.preferredMaxLayoutWidth = self.listIntro.frame.size.width;
[self layoutIfNeeded];
CGFloat height = [self systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
self.frame = ({
CGRect headerFrame = self.frame;
headerFrame.size.height = height;
headerFrame;
});
NSLog(#"height: %#", #(height));
}
When I log height at the end of layoutSubviews, its value is 149 while the label is truncated and numberOfLines is set to 3. After tapping the headerView, setting numberOfLines to 0, and forcing a layout pass, height then gets recorded as 163.5. Great!
The only problem is that the entire headerView doesn't grow, and the cells don't get pushed down.
How can I dynamically change the height of my collectionView's supplementary view (preferably animated)?
I'm aware of UICollectionViewFlowLayout's headerReferenceSize and collectionView:layout:referenceSizeForHeaderInSection: but not quite sure how I'd use them in this situation.
I got something working, but I'll admit, it feels kludgy. I feel like this could be accomplished with the standard CollectionView (and associated elements) API + hooking into standard layout/display invalidation, but I just couldn't get it working.
The only thing that would resize my headerView was setting my collection view's flow layout's headerReferenceSize. Unfortunately, I can't access my collection view or it's flow layout from my instance of UICollectionReusableView, so I had to create a delegate method to pass the correct height back.
Here's what I have now:
// in MyHeaderReusableView.m
//
// my UITapGestureRecognizer's action
- (IBAction)onHeaderTap:(UITapGestureRecognizer *)sender
{
self.listIntro.numberOfLines = 0;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.listTitle.preferredMaxLayoutWidth = self.listTitle.frame.size.width;
self.listIntro.preferredMaxLayoutWidth = self.listIntro.frame.size.width;
CGFloat height = [self systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
self.frame = ({
CGRect headerFrame = self.frame;
headerFrame.size.height = height;
headerFrame;
});
if (self.resizeDelegate) {
[self.resizeDelegate wanderlistDetailHeaderDidResize:self.frame.size];
}
}
// in my viewController subclass which owns the UICollectionView:
- (void)wanderlistDetailHeaderDidResize:(CGSize)newSize
{
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
// this is the key line
flowLayout.headerReferenceSize = newSize;
// this doesn't look beautiful but it's the best i can do for now. I would love for just the bottom of the frame to animate down, but instead, all the contents in the header (the top labels) have a crossfade effect applied.
[UIView animateWithDuration:0.3 animations:^{
[self.collectionView layoutIfNeeded];
}];
}
Like I said, not the solution I was looking for, but a working solution nonetheless.
I ran into the same issue than you, so I was just wondering: did you ever get a solution without the crossfade effect that you mention in the code sample?. My approach was pretty much the same, so I get the same problem. One additional comment though: I managed to implement the solution without the need for delegation: What I did was from "MyHeaderReusableView.m" You can reference the UICollectionView (and therefore, the UICollectionViewLayout) by:
//from MyHeaderReusableView.m
if ([self.superview isKindOfClass:UICollectionView.class]) {
//get collectionView reference
UICollectionView * collectionView = (UICollectionView*)self.superview;
//layout
UICollectionViewFlowLayout * layout = (UICollectionViewFlowLayout *)collectionView.collectionViewLayout;
//... perform the header size change
}

Programmatically scroll UIScrollView using subview element

I would like to know how to access a UIScrollView using a subview UILabel.
I have tried to access the UIScrollView using .superview; however I am now receiving an error
No visible #interface for 'UIView' declares the selector 'scrollRectToVisible:animated:'
The code I am using looks like this
- (void) SymbolButtonPressed:(NSString *)selectedString {
UILabel *label = (UILabel *)[self.view viewWithTag:currentlySelectedTag];
// perform scrolling here, figure out what view your uilable is in.
float newPosition = label.superview.contentOffset.x+label.frame.size.width;
CGRect toVisible = CGRectMake(newPosition, 0, label.superview.frame.size.width, label.superview.frame.size.height);
[label.superview scrollRectToVisible:toVisible animated:YES];
}
The superview of a UILabel is of type UIView and so does not respond to the method you are trying to call. You can cast the superview as a UIScrollView so that Xcode can see the methods and properties you are trying to access. You should also check if the superview responds to the method.
if([label.superview respondsToSelector:#selector(scrollRectToVisible:animated:)]) {
[(UIScrollView *)label.superview scrollRectToVisible:toVisible animated:YES];
}
Given your sample code you will also need to cast the superview to get contentOffset
float newPosition = ((UIScrollView *)label.superview).contentOffset.x+label.frame.size.width;

how to change height of uiview defined in storyboard

Update: I ended up implementing the code below into it's own method and then called it from viewDidLayoutSubviews and willAnimateRotationToInterfaceOrientation. Doing it from viewDidAppear (as suggested) would not resize the view when returning from a segue.
I have a UIView defined in a storyboard which I'm using for a header view on my UIViewController. I have a constant in my code for all header views to be 80 units high. I have a tag on the storyboard header view of 200. I thought I could use this tag to get the view, modify the height of the underlying CGRect, and then re-set the header view to the modified CGRect. That doesn't seem to affect the height however. What am I missing?
- (void)viewDidLoad
{
UIView *header = [self.view viewWithTag:200];
CGRect hrect = [header frame];
hrect.size.height = HEADER_HEIGHT;
[header setFrame:hrect];
...
Try to do that on viewDidAppear then call
[self setNeedsLayout]
The problem is that you're using the tag as an NSString. The tag property is an NSInteger. Try doing the following:
- (void)viewDidLoad
{
UIView *header = [self.view viewWithTag:200];// give the tag as an int
CGRect hrect = [header frame];
hrect.size.height = HEADER_HEIGHT;
[header setFrame:hrect];
...
Also, make sure the tag you defined in the Storyboard is also 200 and not #"200".
Hope this helps!

How do I define a ViewDidLoad method for a UILabel that's contained in a nib file?

I have a nib with a table cell, and within that table cell I have a UILabel. I want to make that label sizeToFit which means I have to do:
- (void)viewDidLoad {
[self sizeToFit];
}
However my label doesn't have any code, just an outlet to a variable in my controller, so there's nowhere I can put that code to effect the label.
I attempted making a sub class of UILabel (fitUILabel : UILabel) and then I clicked on the label in the nib and set its class to fitUILabel, however it does not seem to run.
In my controller right before the return statement in cellForRowAtIndexPath I tried putting
[cell.myLabelOutletVariable sizeToFit]
And this seems to work, however it only works on the recycled rows and not the labels contained in the initial cells of my table. This also seems to cause my text to flow right out of the cells and overlap onto others, however it does align it to the top which is what I wanted.
I assume you mean that your nib contains a UITableViewCell as a top-level object, and the table view cell has a UILabel subview.
The viewDidLoad method is defined on UIViewController, and UITableViewCell doesn't inherit from UIViewController.
UITableViewCell is a subclass of UIView. The proper place for a view to adjust the frames of its subviews is in its layoutSubviews method. You need to make a subclass of UITableViewCell, and set that as the custom class of the cell in your nib. Then, in your subclass, define this method:
- (void)layoutSubviews {
[self.label sizeToFit];
}
In your table view data source's tableView:cellForRowAtIndexPath: method, you may want to send setNeedsLayout to the cell after setting the text of the label. This will ensure that the cell receives layoutSubviews again, if it's being reused.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = ...;
cell.customLabel.text = [self labelTextForIndexPath:indexPath];
[cell setNeedsLayout];
return cell;
}
Alternatively, you could make a UILabel subclass, like this:
#implementation MyLabel
- (void)layoutSubviews {
[self sizeToFit];
}
- (void)setText:(NSString *)text {
[super setText:text];
[self setNeedsLayout];
}
#end
and then set MyLabel as the custom class of the label in your nib. But having a view set its own frame in layoutSubviews seems a little fishy to me, so I usually avoid it.
You should put your code for size the Label in Table view's Delegate Method CellForRowAtIndexPath and not in viewDidLoad.
If your Label is in Table View Cell.
You may rewrite the -(void) awakeFromNib method for initializing the Label.

Resources