Can I adjust the height of the UITableview's separator line? I add UIView at the cell to use as separator line and its good, the problem is that when I slide the cell to delete it, the delete button is the problem, its overlapping the separator line, or can I adjust the delete button's height?
The code pasted in by Rashad is pretty old (found here) and doesn't seem to work for iOS 7 or iOS 8.
Here is updated code that works:
-(void)layoutSubviews {
UIView *deleteButtonView = nil;
for (UIView *subview in self.subviews) {
// find the delete view in iOS 8
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellDeleteConfirmationView"]){
deleteButtonView = subview;
break;
}
// find the delete view in iOS 7
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellScrollView"]) {
for (UIView *secondSubview in [subview subviews]) {
if ([NSStringFromClass([secondSubview class]) isEqualToString:#"UITableViewCellDeleteConfirmationView"]) {
deleteButtonView = secondSubview;
break;
}
}
}
}
int heightOffset = 5;
CGRect buttonFrame = deleteButtonView.frame;
buttonFrame.origin.y = heightOffset;
buttonFrame.size.height = self.frame.size.height-2*heightOffset;
deleteButtonView.frame = buttonFrame;
}
If you can't resize the delete button, resize your bottom UIView so it can overlap the delete button.
I always draw separator line like a subView on contentView of cell. And disable separatorStyle in tableView. And customise delete button like here: https://stackoverflow.com/a/22396248/887325
In you TableViewCell layoutSubviews method write this:
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellDeleteConfirmationControl"]) {
UIView *deleteButtonView = (UIView *)[subview.subviews objectAtIndex:0];
CGRect newf = deleteButtonView.frame;
newf.origin.x = 250;
newf.origin.y = 47;
newf.size.width = 30;
newf.size.height = 50;
deleteButtonView.frame = newf;
}
Hope this helps.. :)
Related
I've been able to change the vertical posiiton of the back button icon but not the text.
I'm using the layoutSubviews method in UINavigationBar:
- (void)layoutSubviews {
[super layoutSubviews];
BOOL fixed = NO;
NSArray *classNamesToReposition = #[#"_UINavigationBarBackIndicatorView", #"UINavigationButton", #"UINavigationItemButtonView"];
for (UIView *view in [self subviews]) {
if ([classNamesToReposition containsObject:NSStringFromClass([view class])] && !fixed) {
CGRect frame = [view frame];
if ([NSStringFromClass([view class]) isEqualToString:#"_UINavigationBarBackIndicatorView"]) {
frame.origin.y = 14.5;
} else if ([NSStringFromClass([view class]) isEqualToString:#"UINavigationButton"]) {
frame.origin.y = 9.0;
} else if ([NSStringFromClass([view class]) isEqualToString:#"UINavigationItemButtonView"]) {
frame.origin.y = 5.0;
}
[view setFrame:frame];
}
}
}
The problem is that any frame change I make on UINavigationItemButtonView does not seem to have any effect, nor any frame change I make on it's UILabel subview that is the actual button text. When I log the views the frames seem to be changing but the text is not moving in my view. What am I doing wrong?
You subclass a UINavigationBar called MyNavigationBar, in layoutSubviews, change the back indicator position.
for (UIView *view in [self subviews]) {
CGRect frame = [view frame];
if ([NSStringFromClass([view class]) isEqualToString:#"_UINavigationBarBackIndicatorView"]) {
frame.origin.y = 19.5; //default is 11.5, move down by 8.
}
[view setFrame:frame];
}
And you can change backBarItem's title position by adding this in applicationDidFinished.
[[UIBarButtonItem appearanceWhenContainedIn:[MyNavigationBar class], nil] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, 8) forBarMetrics:UIBarMetricsDefault];
I tried, in IOS 7 to subclass an UISearchBar so that the place holder is always left aligned.
I did this:
- (void)layoutSubviews {
[super layoutSubviews];
UITextField * tv = self.textField;
tv.textAlignment = NSTextAlignmentLeft ;
}
If tv.textAlignment = NSTextAlignmentRight, then I manage to make the text to the right.
However, the text when UISearchBar is empty, and display the placeholder, always shows to the center.
I wonder why. I put it in layoutSubviews method. Hence, it should be drawn every time the control is drawn.
Can you try this using UIAppearance.
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setTextAlignment:NSTextAlignmentLeft];
Downvoters, please read this : note from Apple:
Note: iOS applies appearance changes when a view enters a window, it doesn’t change the appearance of a view that’s already in a window. To change the appearance of a view that’s currently in a window, remove the view from the view hierarchy and then put it back.
I have same issue before some days ago, but there is no way to change the alignment of search bar's placeholder text, I tested with so many "SO" answer but no one was working.
At last I decided with following Fixing.
Add some white space in Left/right (as you want) of placeholder text
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 6.1) {
// Load resources for iOS 6.1 or earlier
self.searchBar.placeholder = #"hello";
}
else
{
// Load resources for iOS 7 or later
// Add some white space in Left/right *(as you want)* here i added to left
self.searchBar.placeholder = #" hello";
}
Through NSLog the searchBar's textField subviews, can get such a result:
<_UISearchBarSearchFieldBackgroundView: 0x8d492e0; frame = (0 0; 304 28); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x8d49410>> - (null),
<UIImageView: 0x8d488a0; frame = (102.5 7.5; 12.5 12.5); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8d48f80>> - (null),
<UISearchBarTextFieldLabel: 0x8d4b410; frame = (122.5 1; 181.5 25); text = 'hello000000'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8d4b520>>
)
you can see, UISearchBarTextFieldLabel is the UI to show placeholder, here is 'hello000000', which is a subclass of UILabel. Therefore, set searchBar's textField textAlignment will not affect placeholder position directly. And the layout of placeholder label is handled by searchBar's textField - (void)layoutSubviews , whereas you can't override inner textField this method.
A custom searchBar base on UIView, add your subclass of UITextField which override - (void)layoutSubviews, Or even add add UILabel where textfield's text is empty, remove while not. maybe a solution.
Add some my test code:
#interface CustomSearchBar : UIView {
}
#end
#implementation SharedSearchBar
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_textField = [[SearchTextField alloc] initWithFrame:CGRectMake(10, 6, frame.size.width-20, frame.size.height-12)];
_textField.leftView = UIImageViewNamed(#"search_glass");
[self addSubview:_textField];
}
return self;
}
#end
#interface SearchTextField : UITextField
#end
#implementation SearchTextField
- (void)layoutSubviews{
[super layoutSubviews];
for (UIView *subView in self.subviews) {
if ([subView isKindOfClass:[UILabel class]]) {
UILabel *lb = (UILabel *)subView;
lb.textAlignment = UITextAlignmentRight;
lb.text = #"123123";
CGRect frame = lb.frame;
frame.origin.x = textFiled.frame.size.width-frame.size.width;
lb.frame = frame;
break;
}
}
}
#end
In my iOS application i have a scrollview in which there are many imageviews and textviews.
I have to move all this views down to a fixed value, but i don't want to move them to left, right or resize them. I just want to move them down, on an event. The problem is that i can't know which are the values of these UIView.
This is my code:
for(UIView* subview in [myScrollView subviews])
{
subview.frame = CGRectMake(0, 0, screenWidth, screenHeight);
}
i'd like to do this:
for(UIView* subview in [myScrollView subviews])
{
subview.frame = CGRectMake(previousValue+100, previousValue, previousValue, previousValue);
}
I hope I explained myself
If you just want to move them down, why don't you just set scrollView's contentInset?
scrollView.contentInset = UIEdgeInsetsMake(100, previousValue, previousValue, previousValue);
this will make all your subViews down.
try something like this:
UIView *lastView;
for(UIView* subview in [myScrollView subviews])
{
CGrect lastFrame;
if (!lastView) {
lastFrame = CGRectMake(0,0,100,100); // your initial frame
} else {
lastFrame = lastView.frame;
}
lastFrame.origin.x += 100;
subview.frame = lastFrame;
lastView = subview;
}
I have a UIScrollview in a tab, i added some UIView (NIB) to the UIScrollview. Each UIView has some UISwictch, labels, buttons, etc. How can i get the the label.text inside of the view added to the UIScrollview.
I tried a lot of things but i can access to the content of the UIView added to the UIScrollview.
Check with this,
for (UIView *addedView in [self.scrollView subviews])
{
for (UIView *sub in [addedView subviews])
{
if([sub isKindOfClass:[UILabel class]])
{
UILabel *myLabel = (UILabel *)sub;
NSLog(#"My label :%#",myLabel .text);
}
}
}
Here scrollView is your scroll view. It will print all label's text.
If you need to print any particular label's text. Then add a tag to it and check the tag before printing it, like if(myLabel.tag == 7)
set a unique tag for your label.
eg: label.tag = 10345 (some random number, which is a unique tag)
and you can search for the label in the parentView like this
UILabel *theLabel = (UILabel *)[parentView viewWithTag: 10345];
and then you can do what ever you want with the Label.
Step 1: Add this code in your scrollview
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTap:)];
singleTapGestureRecognizer.numberOfTapsRequired = 1;
singleTapGestureRecognizer.enabled = YES;
singleTapGestureRecognizer.cancelsTouchesInView = NO;
[scrollView addGestureRecognizer:singleTapGestureRecognizer];
Step 2: Implement this method
-(void)singleTap:(UITapGestureRecognizer *)gesture
{
// Convert gesture into view for getting subviews
CGPoint point = [gesture locationInView:mainScrollView];
UIView *tappedView = [mainScrollView hitTest:point withEvent:nil];
// Get the subviews of the view
NSArray *subviews = [view tappedView];
// Return if there are no subviews
if ([subviews count] == 0) return;
for (UIView *subview in subviews) {
NSLog(#"%#", subview);
// List the subviews of subview
[self your_method:subview];
}
}
I want my accessory to be in a slightly different place than normal. Is it possible?
This code has no effect:
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.accessoryView.frame = CGRectMake(5.0, 5.0, 5.0, 5.0);
No, you cannot move where the accessory view is. As an alternative you can add a subview like the following;
[cell.contentView addSubview:aView];
Also, by setting the accessoryView property equal to something, the accessoryType value is ignored.
There is a way to move default accessoryView, but it's pretty hacky. So it might stop working one day when a new SDK arrives.
Use at your own risk (this code snippet moves any accessoryView 8 pixels to the left. Call [self positionAccessoryView]; from inside the -(void)layoutSubviews method of the desired UITableViewCell subclass):
- (void)layoutSubviews {
[super layoutSubviews];
[self positionAccessoryView];
}
- (void)positionAccessoryView {
UIView *accessory = nil;
if (self.accessoryView) {
accessory = self.accessoryView;
} else if (self.accessoryType != UITableViewCellAccessoryNone) {
for (UIView *subview in self.subviews) {
if (subview != self.textLabel &&
subview != self.detailTextLabel &&
subview != self.backgroundView &&
subview != self.contentView &&
subview != self.selectedBackgroundView &&
subview != self.imageView &&
[subview isKindOfClass:[UIButton class]]) {
accessory = subview;
break;
}
}
}
CGRect r = accessory.frame;
r.origin.x -= 8;
accessory.frame = r;
}
I was able to change the accessory view's frame by simply doing this in my custom cell subclass.
CGRect adjustedFrame = self.accessoryView.frame;
adjustedFrame.origin.x += 10.0f;
self.accessoryView.frame = adjustedFrame;
Another way to do this is to embed your custom accessory view in another view, that is set as the cell's accessory view and control the padding using the frame.
Here is an example with an image view as custom accessory view:
// Use insets to define the padding on each side within the wrapper view
UIEdgeInsets insets = UIEdgeInsetsMake(24, 0, 0, 0);
// Create custom accessory view, in this case an image view
UIImage *customImage = [UIImage imageNamed:#"customImage.png"];
UIImageView *accessoryView = [[UIImageView alloc] initWithImage:customImage];
// Create wrapper view with size that takes the insets into account
UIView *accessoryWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, customImage.size.width+insets.left+insets.right, customImage.size.height+insets.top+insets.bottom)];
// Add custom accessory view into wrapper view
[accessoryWrapperView addSubview:accessoryView];
// Use inset's left and top values to position the custom accessory view inside the wrapper view
accessoryView.frame = CGRectMake(insets.left, insets.top, customImage.size.width, customImage.size.height);
// Set accessory view of cell (in this case this code is called from within the cell)
self.accessoryView = accessoryWrapperView;
Following the solution given by Ana I tried to better detect the accessory view, I look on the right side of the cell.
Create a custom class that extends UITableViewCell and add this method:
- (void)layoutSubviews {
[super layoutSubviews];
if (self.accessoryType != UITableViewCellAccessoryNone) {
float estimatedAccesoryX = MAX(self.textLabel.frame.origin.x + self.textLabel.frame.size.width, self.detailTextLabel.frame.origin.x + self.detailTextLabel.frame.size.width);
for (UIView *subview in self.subviews) {
if (subview != self.textLabel &&
subview != self.detailTextLabel &&
subview != self.backgroundView &&
subview != self.contentView &&
subview != self.selectedBackgroundView &&
subview != self.imageView &&
subview.frame.origin.x > estimatedAccesoryX) {
// This subview should be the accessory view, change its frame
frame = subview.frame;
frame.origin.x -= 10;
subview.frame = frame;
break;
}
}
}
}
Maybe this will be sufficient for you:
UIImageView* accessoryImageView = [[UIImageView alloc] initWithFrame:
CGRectMake(0, 0, accessoryImage.size.width + MARGIN_RIGHT, accessoryImage.size.height)];
accessoryImageView.contentMode = UIViewContentModeLeft;
accessoryImageView.image = accessoryImage;
self.accessoryView = accessoryImageView;
This way I added padding to the right, so accessory button looks shifted to the left. It has a wider area that responds to touches, that is the only side-effect.
The above answers didn't work for me under ios 6.1. So I tried to use UIEdgeInsets, because the DetailDisclosure is a UIButton. And it works fine now. Here the source:
if (cell.accessoryType == UITableViewCellAccessoryDetailDisclosureButton) {
UIView* defaultAccessoryView = [cell.subviews lastObject];
if ([defaultAccessoryView isKindOfClass:[UIButton class]]){
UIButton *bt = (UIButton*)defaultAccessoryView;
bt.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 10);
}
}
The simple way to set a custom position for the accessoryView that is persisted in any cell status is to layout the accessoryView in layoutSubViews:
- (void)layoutSubviews
{
[super layoutSubviews];
self.accessoryView.center = CGPointMake($yourX, $yourY);
}
I was working with the ios5 and the solution given by Alexey was not working entirely. I discovered that when an accessoryType is set on a table, the accessoryView is null so the first "if" was not working. I have changed a the code just a little:
if (self.accessoryType != UITableViewCellAccessoryNone) {
UIView* defaultAccessoryView = nil;
for (UIView* subview in self.subviews) {
if (subview != self.textLabel &&
subview != self.detailTextLabel &&
subview != self.backgroundView &&
subview != self.contentView &&
subview != self.selectedBackgroundView &&
subview != self.imageView &&
subview != self.explanationButton && // Own button
subview.frame.origin.x > 0 // Assumption: the checkmark will always have an x position over 0.
) {
defaultAccessoryView = subview;
break;
}
}
r = defaultAccessoryView.frame;
r.origin.x -= 8;
defaultAccessoryView.frame = r;
}
and this solution is working for me. As Alexey said, I don't know what is going to happen with future versions but at least in ios 4 is working.
improvements on other answers
For James Kuang, Kappe, accessoryView is nil for default accessory view.
For Matjan, subviews.lastObject is easily the wrong view, like an UITableViewCellSeparatorView.
For Alexey, Ana, Tomasz, enumerating the subviews until we find an unknown one works for now. But it's laborious and could be easily broken in future versions if, let say, Apple adds a backgroundAccessoryView.
For larshaeuser, enumerating the subviews until we find a UIButton is good idea, but contentEdgeInsets is not adequately visibly changing the accessory view.
solution for Swift 3.x and 4.0
We will enumerate and look for the last UIButton.
class AccessoryTableViewCell: UITableViewCell {
override func layoutSubviews() {
super.layoutSubviews()
if let lastButton = subviews.reversed().lazy.flatMap({ $0 as? UIButton }).first {
// This subview should be the accessory view, change its origin
lastButton.frame.origin.x = bounds.size.width - lastButton.frame.size.width - 5
}
}
}
for Swift 4.1 and newer
class AccessoryTableViewCell: UITableViewCell {
override func layoutSubviews() {
super.layoutSubviews()
// https://stackoverflow.com/a/45625959/1033581
if let lastButton = subviews.reversed().lazy.compactMap({ $0 as? UIButton }).first {
// This subview should be the accessory view, change its origin
lastButton.frame.origin.x = bounds.size.width - lastButton.frame.size.width - 5
}
}
}
Here is what I used, this will get rid of the default padding.
override func layoutSubviews() {
super.layoutSubviews()
// Remove the accessory view's default padding.
accessoryView!.frame.origin.x = bounds.width - accessoryView!.bounds.width - safeAreaInsets.right
contentView.frame.size.width = bounds.width - safeAreaInsets.left - safeAreaInsets.right - accessoryView!.bounds.width
}