How to get height of topLayoutGuide? - ios

moviePlayer = MPMoviePlayerController(contentURL: url)
moviePlayer.view.frame = CGRect(x: 0, y:{layoutguide.height}, width:
self.view.frame.width, height: 300)
self.view.addSubview(moviePlayer.view)
viewcontroller have an attribute named "topLayoutGuide", but it seems not I wanted.
I know how to implement in storyboard, in code, I can't get the height of Top Layout Guide. I searched for hours getting nothing.
Below is wanted.

You can get the topLayoutGuide value as its length property:
// Inside your viewController
self.topLayoutGuide.length
Since it is a single value (i.e.: it does not have a height and width) they just called it length. Same holds for bottomLayoutGuide.
Hope this helps
One more thing to mention, in the apple doc for this property:
// As a courtesy when not using auto layout, this value is safe to refer to in -viewDidLayoutSubviews, or in -layoutSubviews after calling super
Use this property in these two functions will get you the accurate value, since the layout has been initialized. If you use it in the viewDidLoad function, this property will be 0.

If someone is looking for how to calculate the height of insets of SafeLayoutGuide available for iOS 11 because Top and Bottom Layout Guide are deprecated now, you can find it in:
view.safeAreaInsets
Notice that Top and Bottom Layout Guide were a part of ViewController, and now SafeLayoutGuide is a part of the main view of the ViewController.

This gets the length (in points) of the portion of a view controller's view that is overlaid by translucent or transparent UIKit bars.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let topSpace:CGFloat
let bottomSpace:CGFloat
if #available(iOS 11.0, *) {
topSpace = self.view.safeAreaInsets.top
bottomSpace = self.view.safeAreaInsets.bottom
} else {
topSpace = self.topLayoutGuide.length
bottomSpace = self.bottomLayoutGuide.length
}
}

Related

UITextview text not centering vertically on initial view

I have an extension for UITextView which centers the text on a UITextView vertically that I found in the following SO answer: https://stackoverflow.com/a/38855122/4660602
My UITextView lives within a UITableViewCell, and the problem is that the function doesn't seem to work on the initial load. It only works when I reload the tableView or when I scroll.
I am calling the method within cellForRowAtIndexPath but I have tried adding it to my custom cell class in awakeFromNib and prepareForReuse but have and no luck. Wondering if anyone has any other advice / solutions.
EDIT:
Also forgot to mention, my VC with the tableView is embedded in a navigationBar and tabBar. When I switch to a new VC in the tabBar and then back, the UITextView text realigns to the top incorrectly, even if it was centered already.
Thanks!
The correct place for sizing code in a view is UIView.layoutSubviews. Since your centering function depends on the bounds, you have to call it in layoutSubviews, otherwise the bounds may not be correct (ie they match whats in the nib and not the current device). You can call setNeedsLayout in your cellforRowAtIndexPath to tell the view to update its layout after you ahve set the text.
In iOS 10, a bound of view hasn't initialized in viewDidLoad or awakeFromNib as before, so functions based on the view's bound don't work in viewDidLoad or awakeFromNib.
You should use it in viewDidAppear or when view's bound has certainly been initialized.
Works for me (Swift 5):
class VerticallyCenteredTextView: UITextView {
override var contentSize: CGSize {
didSet {
var topCorrection = (bounds.size.height - contentSize.height * zoomScale) / 2.0
topCorrection = max(0, topCorrection)
contentInset = UIEdgeInsets(top: topCorrection, left: 0, bottom: 0, right: 0)
}
}
}
The source - https://geek-is-stupid.github.io/2017-05-15-how-to-center-text-vertically-in-a-uitextview/

How to make UIScrollView zoom in only one direction when using auto layout

I'm attempting to make a UIScrollView only allow zooming in the horizontal direction. The scroll view is setup with a pure autolayout approach. The usual approach is as suggested in a number of Stack Overflow answers (e.g. this one) and other resources. I have successfully used this in apps before autolayout existed. The approach is as follows: in the scroll view's content view, I override setTransform(), and modify the passed in transform to only scale in the x direction:
override var transform: CGAffineTransform {
get { return super.transform }
set {
var t = newValue
t.d = 1.0
super.transform = t
}
}
I also reset the scroll view's content offset so that it doesn't scroll vertically during the zoom:
func scrollViewDidZoom(scrollView: UIScrollView) {
scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height)
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0)
}
This works very nicely when not using autolayout:
However, when using autolayout, the content offset ends up wrong during zooming. While the content view only scales in the horizontal direction, it moves vertically:
I've put an example project on GitHub (used to make the videos in this question). It contains two storyboards, Main.storyboard, and NoAutolayout.storyboard, which are identical except that Main.storyboard has autolayout turned on while NoAutolayout does not. Switch between them by changing the Main Interface project setting and you can see behavior in both cases.
I'd really rather not switch off autolayout as it solves a number of problems with implementing my UI in a much nicer way than is required with manual layout. Is there a way to keep the vertical content offset correct (that is, zero) during zooming with autolayout?
EDIT: I've added a third storyboard, AutolayoutVariableColumns.storyboard, to the sample project. This adds a text field to change the number of columns in the GridView, which changes its intrinsic content size. This more closely shows the behavior I need in the real app that prompted this question.
Think I figured it out. You need to apply a translation in the transform to offset the centering UIScrollView tries to do while zooming.
Add this to your GridView:
var unzoomedViewHeight: CGFloat?
override func layoutSubviews() {
super.layoutSubviews()
unzoomedViewHeight = frame.size.height
}
override var transform: CGAffineTransform {
get { return super.transform }
set {
if let unzoomedViewHeight = unzoomedViewHeight {
var t = newValue
t.d = 1.0
t.ty = (1.0 - t.a) * unzoomedViewHeight/2
super.transform = t
}
}
}
To compute the transform, we need to know the unzoomed height of the view. Here, I'm just grabbing the frame size during layoutSubviews() and assuming it contains the unzoomed height. There are probably some edge cases where that's not correct; might be a better place in the view update cycle to calculate the height, but this is the basic idea.
Try setting translatesAutoresizingMaskIntoConstraints
func scrollViewDidZoom(scrollView: UIScrollView) {
gridView.translatesAutoresizingMaskIntoConstraints = true
}
In the sample project it works. If this is not sufficient, try creating new constraints programmatically in scrollViewDidEndZooming: might help.
Also, if this does not help, please update the sample project so we can reproduce the problem with variable intrinsicContentSize()
This article by Ole Begemann helped me a lot How I Learned to Stop Worrying and Love Cocoa Auto Layout
WWDC 2015 video
Mysteries of Auto Layout, Part 1
Mysteries of Auto Layout, Part 2
And so luckily, there's a flag for that. It's called translatesAutoResizingMask IntoConstraints [without space].
It's a bit of a mouthful, but it pretty much does what it says. It makes views behave the way that they did under the Legacy Layout system but in an Auto Layout world.
Although #Anna's solution works, UIScrollView provides a way of working with Auto Layout. But because scroll views works a little differently from other views, constraints are interpreted differently too:
Constraints between the edges/margins of scroll view and its contents attaches to the scroll view's content area.
Constraints between the height, width, or centers attach to the scroll view’s frame.
Constraints between scroll view and views outside scroll view works like an ordinary view.
So, when you add a subview to the scroll view pinned to its edges/margins, that subview becomes the scroll view's content area or content view.
Apple suggests the following approach in Working with Scroll Views:
Add the scroll view to the scene.
Draw constraints to define the scroll view’s size and position, as normal.
Add a view to the scroll view. Set the view’s Xcode specific label to Content View.
Pin the content view’s top, bottom, leading, and trailing edges to the scroll view’s corresponding edges. The content view now defines
the scroll view’s content area.
(...)
(...)
Lay out the scroll view’s content inside the content view. Use constraints to position the content inside the content view as normal.
In your case, the GridView must be inside the content view:
You can keep the grid view constraints that you have been using but, now, attached to content view. And for the horizontal-only zooming, keep the code exactly as it is. Your transform overriding handle it very well.
I'm not sure if this satisfies your requirement of 100% autolayout, but here's one solution, where the UIScrollView is set with autolayout and gridView added as a subview to it.
Your ViewController.swift should look like this:
// Add an outlet for the scrollView in interface builder.
#IBOutlet weak var scrollView: UIScrollView!
// Remove the outlet and view for gridView.
//#IBOutlet var gridView: GridView!
// Create the gridView here:
var gridView: GridView!
// Setup some views
override func viewDidLoad() {
super.viewDidLoad()
// The width for the gridView is set to 1000 here, change if needed.
gridView = GridView(frame: CGRect(x: 0, y: 0, width: 1000, height: self.view.bounds.height))
scrollView.addSubview(gridView)
scrollView.contentSize = CGSize(width: gridView.frame.width, height: scrollView.frame.height)
gridView.contentMode = .ScaleAspectFill
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return gridView
}
func scrollViewDidZoom(scrollView: UIScrollView) {
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0)
scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height)
}

Swift + UIView: How to change y coordinates?

I have a UIView called descriptionView and I want to hide it initially when the screen first loads by offsetting the y coordinate to be the screen size + the height of descriptionView itself:
However, in my controller, none of my frame changes do anything:
override func viewDidLoad() {
super.viewDidLoad()
...
// descriptionView.frame.origin.y = self.view.frame.height
// UIView.animateWithDuration(1, animations: {
// self.descriptionView.frame.origin.y = self.view.frame.height
// self.view.layoutIfNeeded()
// })
//
print("xxx")
descriptionView.frame = CGRectMake(0, self.view.frame.height, self.view.frame.width, 66)
// descriptionView.frame = CGRectOffset(descriptionView.frame, 0, descriptionView.frame.height)
}
No matter what I do it seems fixed at that visible position like in my storyboard. Can someone help?
In IB you are using NSAutoLayout, so you either need to manipulate the constraints, or tell the view to translate the mask to constraints.
If you want to set the frame directly then you will want to do this:
descriptionView.translatesAutoresizingMaskIntoConstraints = true
descriptionView.frame = CGRectMake(...)
Otherwise you can create IBOutlets to the height and width constraint from IB and update those:
self.descriptionViewHeight.constant = self.view.frame.width
Additionally, I would recommend doing frame manipulations inside of viewWillAppear: rather than viewDidLoad. viewDidLoad does not strictly guarantee final position.
Instead of editing the frame or descriptionView, edit its height constraint.
First, create an NSLayoutConstraint from this constraint by cmd-dragging the height constraint from the Interface Builder to your class (like you do for any UI object).
Then you can set the constant property of this constraint to 0.
(Yes, constant is declared as a varproperty...)
If you are using constraints, chaging the frame view myView.frame property will not affect on view actual position and size. Instead of this make constraint outlet and change it in your code, it will look like this:
descriptionView.heightConstraint.constant = 100
Also, if you want to hide something, you can use property myView.hidden = true, or myView.alpha = 0;

How to set full width image inside UIScrollView in swift

I need to display large content in my UIViewController added in Storyboard so I added UIScrollView with constraints top, right, bottom, left:0 ( to make it full screen ).
In top of UIScrollView I need a square image with its width as device screen size, so I added UIImageView with constraints : aspect ratio->1:1, top:0, centre align in container, height : 100.
And below It there is a UILabel where I want to show large text. I am using Auto Layout.
Is there an option to do this in Storyboard ?
In swift I tried below
Connected Image in controller file as :
#IBOutlet weak var eventThumb: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// set image path from url ,it works
eventThumb.image = UIImage(data: NSData(contentsOfURL: NSURL(string: self.event!.image)!)!)
let screenSize: CGRect = UIScreen.mainScreen().bounds
eventThumb.frame = CGRectMake( 0, 0, screenSize.width, screenSize.width)
}
I tried related answers given here ,here and here but they not seem to working in my case.
I am new in swift and ios, am I doing something wrong in structure ?
Edit :
Image added
Call it in:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// your layout code goes here, AFTER the call to super
}
You forgot to call super before your code.
You had tried to make the frame with following screen size
eventThumb.frame = CGRectMake( 0, 0, screenSize.width, screenSize.width)
Please crosscheck it with
eventThumb.frame = CGRectMake( 0, 0, screenSize.width, screenSize.height)
Simply Try this Steps:
First Remove all red lines here you can see in Image
Now keep that line as it is, where you are setting a frame of Imageview
Note:
I already tried this solution in swift & worked for me. I hope it will work for you.
Edited:
As you mentioned you are using Auto layout, So no need to disable it. Just do 1 thing
Put the line which you are setting up the frame of image view in this method:
-(void)viewDidLayoutSubviews

How to setup a view with a half of screen height?

I want to set view1's height to half of the screen's height for all devices (when the device in the portrait mode)
Here is what I want it to look like:
so I make an auto layout of View1's height
#IBOutlet weak var heighConstraint: NSLayoutConstraint!
my viewwillappear function here:
override func viewWillAppear(animated: Bool) {
self. heighConstraint.constant = self.view.frame.size.height / 2
}
But it's not worked when I ran my app. what's wrong in here?
I know the accepted answer is correct but there is simpler method to do so -
Add the view (the one shown in yellow color). Pin it to three edges (top, leading and trailing). I have set it as 0 but change it as per your need.
Create Height Equal to constraint to main View as
Open that Constraint in the Inspector view and edit the multiplier as 0.5.
Setting it as 0.5 takes the height value of half the height of mainView.
Just make sure FirstItem = View1 and SecondItem = SuperView as shown in the image.
Try to change heightConstraint's constant value in viewDidLayoutSubviews method.
override func viewDidLayoutSubviews() {
heightConstraint.constant = self.view.frame.size.height / 2
}
Please try this out. Hope it will help you out. The below line will take the bounds of the device and will set the frame or height according to it.
view1.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/2);
Hope this helps!!! Good Luck...

Resources