Adding space above the first tableview section - objective-c

How do I add space above the first tableview section? You can see that the first section: "A", doesn't have the right above it. Here's an image:
https://docs.google.com/document/d/1QRuL_aoSQNbg-SLlrcAzcQGT1L7gBCTgg-ji-XTHXjY/edit?usp=sharing
Tried changing UITableview content offset in viewDidLoad, but that didn't work. Suggestions?

As UITableView is a subclass of UIScrollView better use contentInset property of your table view as described in "Creating and Configuring Scroll Views" of Apple developer's library.
Simply put this code in a place where you initialize or configure your table view:
CGFloat topInset = 10; //change this value as needed
tableView.contentInset = UIEdgeInsets(top:topInset, left: 0, bottom: 0, right: 0)
UPDATE for Swift 4
let topInset: CGFloat = 10
myTableView.contentInset.top = topInset

Related

Vertical UISlider in iOS with autolayout

As per my iPad app requirement, i've to show the UISlider vertically.
I'm using iOS7 compiler and deployment target is iOS6.
In the story board I added horizontal UISlider of width 600 pixels. I created IBOutlet in my view controller. I didn't set any auto layout constraints. I'm using the below code to rotate and make it vertical.
self.slider.transform = CGAffineTransformMakeRotation(M_PI_2);
After and before rotation I'm printing the frame size of the slider which is correct. But the slider is not looking proper. Its just showing only knob in the center. How can I rotate the UISlider?
I got a vertical slider working with iOS 8 and Xcode 6 with only 3 constraints in the storyboard and one line of code. Here's a cropped screencap of the interface:
There are 3 constraints between the vertical slider and the UIImageView next to it:
vSlider.Center Y = Image View.Center Y
vSlider.Width = Image View.Height
vSlider.Center X = Image View.Trailing + 16
And of course the one line of code is:
self.vSlider.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2))
It's easy to set up these constraints in the storyboard in Xcode 6, but I think it should be simple to write these constraints in code to support iOS 7 or 6.
I got it to work this way:
In viewDidLoad: I added
[self.slider removeConstraints:self.slider.constraints];
[self.slider setTranslatesAutoresizingMaskIntoConstraints:YES];
so that it's called before rotating the slider with
self.slider.transform=CGAffineTransformRotate(self.slider.transform,270.0/180*M_PI);
and there is no need to remove and re-add it to superview.
This is an old topic, but here is a Swift solution with autolayout constraints in storyboard and nothing else.
1/ You need to add rotation to the IBOutlet:
#IBOutlet weak var mySlider: UISlider! {
didSet {
mySlider.transform = CGAffineTransform(rotationAngle: -CGFloat.pi/2)
} // didSet
} // IBOutlet
2/ Define in storyboard the constraints, keeping in mind that the Slider will be rotated around its center.
For instance if you want to locate mySlider on the left side of myView, you need three constraints.
myView.Leading = mySlider.CenterX - 20
mySlider.width = myView.Height (with a multiplier of 0.8 for instance)
mySlider.CenterY = myView.CenterY
mySlider will of course appear horizontal in storyboard, but will have the correct sizing, and the center will be correctly positioned.
Uncheck Auto-Layout on your ViewController, there is no other option under the SDK 7.0 to make it work vertically :(
There are so many possible solutions around about putting UISlider vertical. Here is my summary for iOS7 in XCode5 with autoLayout enabled(defaultly in storyboard):
in viewDidLoad add method
self.slider.transform = CGAffineTransformMakeRotation(M_PI_2);
define your autoLayout constraints about slider explicitly in storyboard as whatever you like
In your viewDidLoad, try:
UIView *superView = self.sizeSlider.superview;
[self.sizeSlider removeFromSuperview];
[self.sizeSlider removeConstraints:self.view.constraints];
self.sizeSlider.translatesAutoresizingMaskIntoConstraints = YES;
self.sizeSlider.transform = CGAffineTransformMakeRotation(M_PI_2);
[superView addSubview:self.sizeSlider];
It does not work with constraints, so the trick is to remove the constraints for your uislider.
You might have to resize it manually by setting its frame property.
You can't use storyboard to build up a UISlider.
Build up UISlider by coding.
slider = [[UISlider alloc] initWithFrame:CGRectMake(640, 150, 600, 400)];
[slider.layer setAnchorPoint:CGPointMake(0.0f, 0.0f)];
slider.transform = CGAffineTransformMakeRotation(M_PI/2);
[self.view addSubview:slider];
Try this :-
self.slider.transform=CGAffineTransformRotate(slideToUnlock.transform,-90.0/180*M_PI);
Try below code to Rotate the UISlider in Vertical Position..
//To rotate the slider in Vertical Position
CGAffineTransform sliderRotation = CGAffineTransformIdentity;
sliderRotation = CGAffineTransformRotate(sliderRotation, -(M_PI / 2));
sliderBrightness.transform=sliderRotation;
For me a two-step process worked best (incorporating some of the previous solutions)
Autolayout step)
I added a vertical view in IB and used autolayout to link it to neighboring views. Then I added a slider in the view and simply hooked it up to the center of the view. Then hooked up the width of the slider to the height of the view. Finally control-dragged the slider outlet to my ViewController code (as slider)
Code step)
Then simply added the to my viewWillAppear (swift-code):
let trans = CGAffineTransformMakeRotation(CGFloat(M_PI_2));
slider.transform = trans;

Setting UITableView ContentInset

I am changing content insets bottom value manually.
literatureTableView.contentInset = UIEdgeInsetsMake(0, 0, 100, 0);
This works fine but i want to set the value depending on the height of the table contents. I tried some things with tableView.bounds.size.height etc. but nothing is working.
If you need to know the calculated bottom inset prior to displaying the reloaded data, then you must calculate the heights manually.
However, if your app permits you to call reloadData and then adjust the inset, you can arrive at it much more simply by doing this:
CGFloat trueContentHeight;
CGFloat heightWithInsets = self.tableView.contentSize.height;
UIEdgeInsets insets = self.tableView.contentInset;
trueContentHeight = heightWithInsets - (insets.top + insets.bottom);
Then you can calculate the bottom inset with the trueContentHeight and the height of the tableView's bounds.
youll need to add up the height of your cells and headers (if you have any). You can use
tableView:heightForRowAtIndexPath: from self to get the height's manually and add them up if they are not all the same.

How do you move the legal sign in mapview

I wonder if anyone know how you move the legal sign on a mapview, right now my toolbar is covering it. Does anyone know how? There is lot's of help with the google logo but nothing on the apple maps.
In Swift:
mapView.layoutMargins = UIEdgeInsetsMake(top, right, -20, left)
I tested this in OS9 and it works.
Swift 5.2
// -20 will make the legal disclaimer move down. If you want the
// disclaimer to move up, use a positive number.
mapView.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: -20, right: 0)
This should work, although I'm not sure whether Apple will allow you to do that
UILabel *attributionLabel = [mapView.subviews objectAtIndex:1];
attributionLabel.center = CGPointMake(attributionLabel.center.x, attributionLabel.center.y - 44.0f);
This is still possible in iOS 7, but only (?) if placed in viewDidAppear.
The coords are reset if placed in viewDidLoad or viewWillAppear.
UILabel *attributionLabel = [mapView.subviews objectAtIndex:1];
attributionLabel.center = CGPointMake(attributionLabel.center.x, attributionLabel.center.y - 44.0f);
These methods no longer work on iOS 7. Correct way is to specify bottomLayoutGuide on your UIViewController.
Described in detail here
Changing the position doesn't quite work, however hiding the "Legal" button works perfectly.
[[mapView.subviews objectAtIndex:1] setHidden:YES]
EDIT:
Swift 2.0 iOS equivalent
mapView.subviews[1].isHidden = true
Carrying on Skeet Skeet point .
I implemented your approach it worked well but after coming in the viewcontroller multiple times legal label y keeps on decreasing as you see the logic it always displaces itself. so instead changing centre i propose
we change frame
UILabel *attributionLabel = [mapView.subviews objectAtIndex:1];
attributionLabel.frame = CGRectMake(20, self.view.frame.size.height - 135, attributionLabel.frame.size.width, attributionLabel.frame.size.height);
\\135 is height of your bottom view that was hiding legal
I wrote extension that worked for me. It can be used in animation block to animate those changes:
import MapKit
extension MKMapView {
/// Workaround for layoutMargins bug.
func setLegalInsets(left: CGFloat, bottom: CGFloat) {
let oldLeft = layoutMargins.left
let oldBottom = layoutMargins.bottom
let lblLegal = (subviews.filter { view in
return view is UILabel
}).first
lblLegal?.frame.origin.x += left - oldLeft
lblLegal?.frame.origin.y -= bottom - oldBottom
layoutMargins.left = left
layoutMargins.bottom = bottom
}
}
#Dymtro's answer works well for me, but I would suggest checking the size of the subviews first. This should at least prevent possible crashes if the view hierarchy changes in the future:
override func viewWillLayoutSubviews() {
positionLegalMapLabel()
}
func positionLegalMapLabel() {
if self.mapView.subviews.count > 1 {
let legalMapLabel = self.mapView.subviews[1]
legalMapLabel.frame.origin = CGPointMake(self.mapView.bounds.size.width - legalMapLabel.frame.size.width - 7, legalMapLabel.frame.origin.y)
}
}
Swift 4+
You can change the position of those by setting the layoutMargins of the mapView.
For example this will push it off from the bottom:
mapView.layoutMargins.bottom = -100
Also you can change edge insets you need all at once:
mapView.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: -100, right: 0)
A Swift 3 example based on #xeieshan's example that works when compiled against iOS 10 SDK. In my example I have a transparent bar in the bottom that animates up when the map view is being present. The label repositioning can also be animated.
// reposition the 'Legal' label above the transparent bottom bar
// unfortunately there is no safe way to identify the label but it is the last subview - hopefully this will not change 😱
if let legalLabel = mapView.subviews.last {
var frame = legalLabel.frame
frame.origin.y = frame.origin.y - self.bottomBar.bounds.size.height // reposition it above the bottom bar
legalLabel.frame = frame
}
Use viewWillLayoutSubviews() instead of viewDidAppear() to avoid a jump.
override func viewWillLayoutSubviews() {
positionLegalMapLabel()
}
func positionLegalMapLabel() {
let legalMapLabel = self.mapView.subviews[1]
legalMapLabel.frame.origin = CGPointMake(self.mapView.bounds.size.width - legalMapLabel.frame.size.width - 7, legalMapLabel.frame.origin.y)
}

UIScrollView contentSize not working

I put a UIScrollView in my nib's view, and linked it to a an IBOutlet property.
Now, when I do this in my viewDidLoad method, it seems to have no effect on the contentSize:
self.sv.backgroundColor = [UIColor yellowColor]; // this works
CGSize size = CGSizeMake(1000.0, 1000.0);
[self.sv setContentSize:size]; // this does not
It behaves as if the contentSize was the same as the frame. What's going on?
This started working when I turned off AutoLayout. Why?
I had the same problem. Auto Layout for UIScrollView is messed up.
Work around: Put everything in the UIScrollView into another UIView, and put that UIView as the only child of the UIScrollView. Then you can use Auto Layout.
If things near the end is messed up (the end of whichever direction your UIScrollView scrolls), change the constraint at the end to have the lowest possible priority.
I tried viewWillLayoutSubviews to update scrollView's contentSize, it worked for me.
- (void)viewDidLayoutSubviews
{
[self.bgScrollView setContentSize:CGSizeMake(320, self.view.frame.size.height* 1.5)];
}
Apple Doc
-(void)viewDidLayoutSubviews
Called to notify the view controller that its view has just laid out its subviews.
Discussion
When the bounds change for a view controller’s view, the view adjusts the positions of its subviews and then the system calls this method. However, this method being called does not indicate that the individual layouts of the view’s subviews have been adjusted. Each subview is responsible for adjusting its own layout.
Your view controller can override this method to make changes after the view lays out its subviews. The default implementation of this method does nothing.
The easiest/cleanest way is to set contentSize at viewDidAppear so you negate the effects of autolayout. This doesn't involve adding random views. However relying on load order for an implementation to work may not be the best idea.
Use this code. ScrollView setContentSize should be called async in main thread.
Swift:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
DispatchQueue.main.async {
var contentRect = CGRect.zero
for view in self.scrollView.subviews {
contentRect = contentRect.union(view.frame)
}
self.scrollView.contentSize = contentRect.size
}
}
Objective C:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
dispatch_async(dispatch_get_main_queue(), ^ {
CGRect contentRect = CGRectZero;
for(UIView *view in scrollView.subviews)
contentRect = CGRectUnion(contentRect,view.frame);
scrollView.contentSize = contentRect.size;
});
}
There are two problems here. (1) viewDidLoad is too soon; you have to wait until after layout has taken place. (2) If you want to use autolayout with a scrollview that comes from a nib, then either you must use constraints to completely describe the size of the contentSize (and then you don't set the contentSize in code at all), or, if you want to set it in code, you must prevent the constraints on the scrollview's subviews from dictating the contentSize. It sounds like you would like to do the latter. To do so, you need a UIView that acts as the sole top-level subview of the scrollview, and in code you must set it to not use autolayout, enabling its autoresizingMask and removing its other external constraints. I show an example of how to do that, here:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch20p573scrollViewAutoLayout/ch20p573scrollViewAutoLayout/ViewController.m
But notice also the next example, which shows how to use constraints completely, instead of contentSize.
A SUPER easy way to use AutoLayout with UIScrollViews inside Interface Builder:
Step 1: Create a UIScrollView
Step 2: Create a UIView that is a child of your scroll view like so:
-UIScrollView
---UIView
-----Your other content
(We'll call this one contentView).
Step 3: In the size inspector, give this view a height and width (say, 320x700).
Step 4 (using AutoLayout): Create unambiguous constraints from your contentView to its superview (the UIScrollView): connect the 4 edges (top, leading, trailing, bottom), then give it a defined width and height that you want it to scroll too.
For example: If your scroll view spans the entire screen, you could give your content view a width of [device width] and a height of 600; it will then set the content size of the UIScrollView to match.
OR:
Step 4 (not using AutoLayout): Connect both of these new controls to your view controller using IB (ctrl+drag from each control to your view controller's .h #implementation). Let's assume each is called scrollView and contentView, respectively. It should look like this:
#interface YourViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#property (strong, nonatomic) IBOutlet UIView *contentView;
#end
Step 5 (not using AutoLayout): In the view controller's .h file add (actually, override) the following method:
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.scrollView.contentSize = self.contentView.frame.size;
}
You can use this lines of code into your *.m file's
- (void)viewDidLoad{
[scroll setContentSize:CGSizeMake(320, 800)] ;
[scroll setScrollEnabled:TRUE];
[scroll setShowsVerticalScrollIndicator:NO];
[scroll setShowsHorizontalScrollIndicator:YES];
}
for this you need to take an IBOutlet property of UIScrollView into your *.h file this way:
IBOutlet UIScrollView *scroll;
And connect this from Storyboard.
Or,
You can use this method into your *.m file:
-(void)viewDidLayoutSubviews
{
[scroll setContentSize:CGSizeMake(320, self.view.frame.size.height* 1.5)];
// this will pick height automatically from device's height and multiply it with 1.5
}
This both solution works for me in xcode-5, xcode-6, xcode-6.1, xcode-6.2
Setting the contentSize in viewDidAppear is critical.
But I also had a variation of what worked in the 3.5 inch screen, and the 4 inch screen. The 4 inch screen worked, the older one does not. Both iOS 7. Bizarre is an understatement!
I could never get auto layout based on constraints to work. Since my view was already a subclass UIScrollView I solved it by overriding setContentView: and ignoring auto layouts zero height setContentSize: message.
#interface MyView : UIScrollView {}
#end
#implementation MyView
- (void)setContentSize:(CGSize)aSize {
if (aSize.height > 0)
[super setContentSize:aSize];
}
#end
I used to do set up the uiscrollview programmatically UNTIL I watched the following wonderful tutorial, step by step how to get uiscrollview and uiview to work: https://www.youtube.com/watch?v=PgeNPRBrB18
After watching the video you will start liking Interface Builder I am sure.
Vote up
Still not scrolling when dynamic height of labels exceeds view height.
I did what yuf's answer marked as correct above said to do (I added a content view to my scrollview and set the constraints leading, trailing, top bottom, and equal widths from the content view to the scroll view.) but still my view was not scrolling when the internal controls height exceeded the height of the scrollview.
Inside my content view I have an image and 3 labels below it. Each label adjusts their own height dependant on how much text is in them (they are set to word-wrap and numberoflines = 0 to achieve this).
The problem I had was my content view's height was not adjusting with the dynamic height of the labels when they exceeded the height of the scroll view/main view.
To fix this I worded out I needed to set the Bottom Space to Container constraint between my bottom label and the contentview and gave it a value of 40 (chosen arbitrarily to give it a nice margin at the bottom). This now means that my contentview adjusts its height so that there is a space between the bottom of the last label and itself and it scrolls perfectly!
Yay!
Try this out...
add all constraints like you do for UIView (See screenShot of my ViewControler in Storyboard)
Now trick begins. select your last object and select its bottom constraint. (See above screenShot, Instagram button's Bottom Constraint(Yellow line)) and Change the Constant in Size Inspector like in bellow screenshot.
i require Constant=8 but you can change as per your requirements.
this Constant is the Space between That Orange Button's Bottom and the scrollView.
EDIT
Make Sure about your view's hierarchy .
0) ViewController.view (optional)
1) UIScrollView
2) UIView (Rename as "contentView")
3) UIView (this view is your content that will make scrollView scroll)
I finally worked out my own solution to this problem because in my case I couldn't use the view controller's life cycle. Create your own scroll view subclass and use it instead of UIScrollView. This even worked for a scroll view inside a collection view cell.
class MyScrollView:UIScrollView {
var myContentSize:CGSize = CGSize.zero // you must set this yourself
override func layoutSubviews() {
super.layoutSubviews()
contentSize = myContentSize
}
}
My MyScrollView was defined in the nib with a tag of 90. If so this is a good way to set content size in the code in the parent view.
let scrollView = viewWithTag(90) as! MyScrollView
scrollView.myContentSize = ...
If you are using AutoLayout a really easy way to set the contentSize of a UIScrollView is just to add something like this:
CGFloat contentWidth = YOUR_CONTENT_WIDTH;
NSLayoutConstraint *constraintWidth =
[NSLayoutConstraint constraintWithItem:self.scrollView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeLeading
multiplier:1
constant:contentWidth];
[self.scrollView addConstraint:constraintWidth];
I got Autolayout to work for paginated scroll views whose pages occupy the full-width of the screen. The pages automatically resize according to the scroll view's size. I haven't tested this for lesser-width scroll views but do comment away if it works--I beleieve it should. Targeted for iOS 9, wrote code in Swift 2, used a mix of IB's and custom code in awakeFromNib.
Steps:
Define a full-screen scroll view.
Inside the scroll view, add a UIView (I called mine contentView) whose top, trailing, bottom, and leading edges to the scroll view are all zero; the height is equal to the scroll view's; but the width is the scroll view's width times the number of pages. If you're doing this visually, you will see your content view extend beyond your scroll view in Inteface Builder.
For every "page" inside the contentView, add Autolayout rules to put them side-by-side each other, but most importantly, give them each a constraint so that their widths are equal to the scroll view's, not the content view's.
Sample code below. embedChildViewController is just my convenience method for adding child VCs--do look at setupLayoutRulesForPages. I have exactly two pages so the function is too simple, but you can expand it to your needs.
In my view controller:
override func loadView() {
self.view = self.customView
}
override func viewDidLoad() {
super.viewDidLoad()
self.embedChildViewController(self.addExpenseVC, toView: self.customView.contentView, fillSuperview: false)
self.embedChildViewController(self.addCategoryVC, toView: self.customView.contentView, fillSuperview: false)
self.customView.setupLayoutRulesForPages(self.addExpenseVC.view, secondPage: self.addCategoryVC.view)
}
My custom view:
class __AMVCView: UIView {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var contentView: UIView!
#IBOutlet weak var pageControl: UIPageControl!
override func awakeFromNib() {
super.awakeFromNib()
self.scrollView.pagingEnabled = true
self.scrollView.bounces = true
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.showsVerticalScrollIndicator = false
self.pageControl.numberOfPages = 2
self.contentView.backgroundColor = UIColor.blueColor()
self.scrollView.backgroundColor = UIColor.clearColor()
self.backgroundColor = UIColor.blackColor()
}
func setupLayoutRulesForPages(firstPage: UIView, secondPage: UIView) {
guard self.contentView.subviews.contains(firstPage) && self.contentView.subviews.contains(secondPage)
else {
return
}
let rules = [
"H:|-0-[firstPage]-0-[secondPage]-0-|",
"V:|-0-[firstPage]-0-|",
"V:|-0-[secondPage]-0-|"
]
let views = [
"firstPage" : firstPage,
"secondPage" : secondPage
]
let constraints = NSLayoutConstraint.constraintsWithVisualFormatArray(rules, metrics: nil, views: views)
UIView.disableAutoresizingMasksInViews(firstPage, secondPage)
self.addConstraints(constraints)
// Add the width Autolayout rules to the pages.
let widthConstraint = NSLayoutConstraint(item: firstPage, attribute: .Width, relatedBy: .Equal, toItem: self.scrollView, attribute: .Width, multiplier: 1, constant: 0)
self.addConstraint(widthConstraint)
}
}

Size a UIToolbar to fit its items?

I'm trying to figure out a clean way to make a UIToolbar only as wide as it needs to be to fit the items that it contains. Is there a way to either:
Configure the UIToolbar to adjust its width automatically as its items are changed?
Programatically determine the minimum width required by the items, which can then be used to set the UIToolbar's frame?
I haven't been able to figure this out due to the spacing between the items and the fact that UIBarButtonItems are not UIView subclasses.
After trying some suggestions from other answers that did not work unless I used custom views, or unless everything was loaded, I finally arrived at a way to set the toolbar width based on its items:
//Add bar items to toolbar first.
UIView* v = toolbar.subviews.lastObject;
float newWidth = v.frame.origin.x + v.frame.size.width;
//Set toolbar width
You'll need to override UIToolbar -setItems: or otherwise detect changed buttons to autoresize.
I have included this feature in my refactoring library, es_ios_utils, to set a navigation item's right item with multiple buttons. In the preceding link, see UIToolbar +toolbarWithItems: and UINavigationItem -setRightBarButtonItems.
I just checked the documentation and seems like UIBarButtonItems, even though they are not UIView subclasses, they have an attribute width. I didn't try it myself, but I think you could sum each item's width, including the flexible item, and get the width for your toolbar.
Hope it helps! ;)
Swift 3, 4 update
I have a toolbar with barButtonItem initiated with image. All I need to do is to calculate the total width of the images plus the intervals (margin.)
Margin is by default 11 in width. The code will be:
let width: CGFloat = YourBarButtonItemList.reduce(0) {sofar, new in
if let image = new.image {
return sofar + image.size.width + 11
} else {
return sofar + ADefaultValueIfThisDoesntWork + 11
}
}