getting wrong center of frame for detail view for UISplitView - ios

learning SWIFT. I can't seem to access bounds from my view... no matter what platform I work in the bounds return the same values (600,600).
I have a graphView : UIView & controller embedded in a navigationController as the Detail of a SplitViewController.
I am trying to get the center of my graphView : UIView on screen (the Detail of the splitViewController), but center keeps returning a point too far to the right (in portrait)/bottom (in landscape).
I tried accessing it multiple ways, but maybe my understanding of it is wrong?
example: var screenCenter: CGPoint = convertPoint( center, fromView: superview)
println("bounds are \(bounds)") // view boundaries
println("frame is at \(frame)") // frame where the view resides
println(" center is at \(center)")
println(" ScreenCenter is at \(screenCenter)")
(output)
bounds are (0.0,0.0,600.0,600.0)
frame is at (0.0,0.0,600.0,600.0)
center is at (300.0,300.0)
ScreenCenter is at (300.0,300.0)
-> how do I actually get to the center? Is SplitView geography joint master + detail?
-> it seem the x positioning is correct in portrait (but not the y), and the y is correct in portrait (but not the x)

SOLVED! the graphView in Storyboard was missing constraints and using the default 600,600 values. "Reset to Suggested Constraints" in storyboard with the view selected fixed the issue

If you do not use storyboard this can help.
Create a view where you set up the autoconstraints to false.
Constraint the view to the safeAreaLayoutGuide.It will find the centre of your secondary view or detail view.
var logoView :UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFit
iv.backgroundColor = .green
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(logoView)
logoView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
logoView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
logoView.heightAnchor.constraint(equalToConstant: 200).isActive = true
logoView.widthAnchor.constraint(equalToConstant: 200).isActive = true
}

Related

Does self.view has constraints?

self.view is on the ViewController. Does it have any constraints? I found it had a top and a left constraint. It has a frame for sure. The autolayout system starts from self.view. I mean until self.view the iOS system is still using frame for positioning the views. After that, our views are able to be added with autolayout. So in the viewcontroller, I use self.frame.size.width for the width constraint for my own view is the correct approach to do? Am I right?
Use SCREEN_WIDTH and It's very easy to get your device size and manage your own view properly:
let SCREEN_WIDTH : CGFloat = UIScreen.main.bounds.width
let SCREEN_HEIGHT : CGFloat = UIScreen.main.bounds.height
For more information you can refer this : link
Example :
You should add your own new view programatically.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let SCREEN_WIDTH : CGFloat = UIScreen.main.bounds.width
let SCREEN_HEIGHT : CGFloat = UIScreen.main.bounds.height
let newView = UIView()
newView.translatesAutoresizingMaskIntoConstraints = false
newView.backgroundColor = UIColor.black
newView.frame.size = CGSize(width: SCREEN_WIDTH * 0.5 , height: SCREEN_HEIGHT * 0.5)
self.view.addSubview(newView)
self.view.layoutIfNeeded()
}
}
Here I am giving the frame to the constraints of my customized view. I don't want to give this value in a format of frame.size. I want to use somethings like constraint.constant. I mean pure autolayout.
self.view.frame is only accurate once viewDidLayoutSubviews() is called for the first time, typically just before viewDidAppear meaning if you try and access in viewDidLoad or first time viewWillAppear the frame will be wrong.
So you should just check in viewDidLayoutSubviews() (but note that function is called whenever the layout changes (this does not mean you scroll on a table view, it means the actual constraints change bc of a screen rotation for ex, or otherwise)
If you can't wait until that method or have some code that's not idempotent and don't want to have some like hasSetup bool to make sure you only do it once... Then you can either access the screen size directly via UIScreen.main.bounds
OR
I wouldn't necessarily recommend the following but for the sake of completeness I've also gotten away with tricks to get the view to layout earlier like calling view.layoutIfNeeded() earlier in the view lifecycle or on the view controller that initializes:
let myVC = MyViewController()
_ = myVC.view // fixes frame for some reason
Using AutoLayout correctly here
You want to use constraints but the frame shouldn't matter at all if you make relative constraints. Aka instead of saying height = constant you would say "make this the same height as vc.view" or "make the leading edge of this view 20 pixels right of the leading edge of vc.view".
something like:
let myView = UIView()
self.view.addSubview(myView)
myView.translatesAutoresizingMaskIntoConstraints = false
myView.heightAnchor.constraint(equalTo: self.view.heightAnchor)
myView.leadingAnchor.constraint(equalTo: vc.view.leadingAnchor, constant: 20.0) // 20 pixels to right of vc.view leading

self.view = mainView vs self.view.addSubview(mainView) with respect to corner radius

I have a UIView in which I have a circular border around it. Here is the code for it:
let v = UIView()
self.view.addSubview(v)
v.backgroundColor = .orange
v.translatesAutoresizingMaskIntoConstraints = false
v.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
v.rightAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
v.topAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
self.view.layoutIfNeeded()
v.layer.cornerRadius = v.frame.width / 2
If I set the ViewController view like this: self.view = mainView (mainView is a subclass of MainView which contains some other subviews), then the result of the corner radius is no longer a circle: Resulting "circle".
However, if I use self.view.addSubview(mainView) (and add autolayout constraints to mainView) and replace self.view.addSubview(v) with self.mainView.addSubview(v) then the circle turns out to be fine.
Why does the circle turn out weird only when I set self.view = mainView, but is fine when I do self.view.addSubview(mainView)?
First where are you replacing the UIViewControllers view with mainView? You should be overriding the loadView method of the UIViewController not doing it in the viewDidLoad method.
Second if you are changing the UIViewControllers view then it will not have it's frame setup when you get to viewDidLoad like the original will and so the UIView v is getting the incorrect layout size which it is then using to determine its corner radius.
Third you shouldn't use layoutIfNeeded in the viewDidLoad as this is way to early to be trying to determine the final layout of the main view (everything is still loading). What you should be doing is overriding viewDidLLayoutSubviews method and setting the corner radius there like this:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
v.layer.cornerRadius = v.frame.width / 2
}
What that also does is if the size of the UIView v changes (orientation change, etc) then it will remain a circle at it's new size otherwise it will just have rounded corners.
Of course you have to make UIView v a class instance variable in order to be able to access it here.

Dynamic corner radius for a UITextView

I am trying to make my uitextview's corner radius dynamic depending on the number of lines displayed. The text is retrieved from my backend, so the number of lines will vary...
I've tried setting the corner radius in viewDidLoad after the text view is created by using its frame but for some reason that doesn't work. I'm assuming its returning 0 for some reason.
Any help is greatly appreciated.
(I'm cutting out a lot of code just to keep it simple here. Everything is added to the subview properly. Everything is added programmatically - not using the storyboard at all here. The text views display as expected besides the corner radius)
inside viewDidLoad:
questionOneTextBox.layer.cornerRadius = self.questionOneTextBox.frame.height * 0.5
questionTwoTextBox.layer.cornerRadius = self.questionTwoTextBox.frame.height * 0.5
If you are creating the text view programmatically with constraints, then you need to call self.questionOneTextBox.layoutIfNeeded() and self.questionTwoTextBox.layoutIfNeeded(). This will initialise the bounds.
class ViewController: UIViewController {
var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Create the Text View
self.textView = UITextView()
self.textView.text = "This text will appear in the text view."
self.textView.backgroundColor = .lightGray
self.view.addSubview(self.textView)
// Set the constraints
self.textView.translatesAutoresizingMaskIntoConstraints = false
self.textView.widthAnchor.constraint(equalToConstant: 280).isActive = true
self.textView.heightAnchor.constraint(equalToConstant: 60).isActive = true
self.textView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 60).isActive = true
self.textView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
// This will initialise the bounds
self.textView.layoutIfNeeded()
// Now the this should work
self.textView.layer.cornerRadius = self.textView.frame.height * 0.5
}
}
This what this code looks like.
Views are not laid out (sized) when viewDidLoad is called. Move your corner radius sizing to viewDidLayoutSubviews or viewWillAppear, at which time the sizing has occurred.
Did you set clipsToBounds to true? This might be causing your problem. I hope this helped

UIScrollView not scrolling at all when added programmatically in Swift?

So, I have looked through almost all of Stackoverflow's answers to this particular question and even looked through tutorials that supposedly teach you how to use a scroll view but It doesn't seem to apply for my project..
Here is what I know so far, in order for a Scroll View to properly work you first need to give it a content size. This determines the scrollable height etc.
I have provided some code to give you all a better idea of how I am adding said items into my scrollview. If there is something that I am doing wrong or if there is a better way to go about doing this please let me know, I am still fairly new to Swift and iOS development and in my mind it feels like I am doing it correctly.
The steps I am taking
Create items that I want to display (Input fields, Imageviews etc..)
Add said items to the view of the viewcontroller. (view.addsubview(etc..))
Create a scrollView and set its constraints to be same as the screen / view
Add our view with all the items in it into said scroll view
Relax and everything should work out perfect?????
Here is my code, I know it might be lengthy but I think it might be needed so that the scope of my question is understood
class JobRegistrationController: UIViewController {
// ... Omitted for clarity
lazy var scrollView: UIScrollView = {
let view = UIScrollView(frame: UIScreen.main.bounds)
view.backgroundColor = .red
view.contentSize = CGSize(width: self.view.bounds.width, height: self.view.bounds.height * 2)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
//... Omitted for clarity
let scrollContentView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// Need so that view controller is not behind nav controller
self.edgesForExtendedLayout = []
view.addSubview(scrollView)
scrollView.addSubview(scrollContentView)
scrollContentView.addSubview(jobTypeField)
scrollContentView.addSubview(jobTypeDividerLine)
// x, y, width and height constraints
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
// x, y, width and height constraints
scrollContentView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollContentView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollContentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollContentView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
// x, y, width and height constraints
jobTypeField.leftAnchor.constraint(equalTo: scrollContentView.leftAnchor, constant: 20).isActive = true
jobTypeField.topAnchor.constraint(equalTo: scrollContentView.topAnchor).isActive = true
jobTypeField.rightAnchor.constraint(equalTo: scrollContentView.rightAnchor, constant: -8).isActive = true
jobTypeField.heightAnchor.constraint(equalToConstant: 30).isActive = true
// x, y, width and height constraints
jobTypeDividerLine.leftAnchor.constraint(equalTo: scrollContentView.leftAnchor, constant: 20).isActive = true
jobTypeDividerLine.topAnchor.constraint(equalTo: jobTypeField.bottomAnchor).isActive = true
jobTypeDividerLine.rightAnchor.constraint(equalTo: scrollContentView.rightAnchor).isActive = true
jobTypeDividerLine.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
Use this method in your class
override func viewDidLayoutSubviews()
{
scrollView.delegate = self
scrollView.contentSize = CGSize(width:self.view.frame.size.width, height: 1000) // set height according you
}
view.contentSize = CGSize(width: self.view.bounds.width, height: self.view.bounds.height * 2)
You should try to log the contentSize in your console after trying to access it. I am not sure if you are setting the correct contentSize here if the self.view.bounds has been calculated correctly when this gets called at that moment. Since it takes time for self.view frame and bounds to be calculated.
Try setting your contentSize after you have added the actual content to it based on the actual total content size.
EDIT:
Add a single UIView inside the scrollView, with the constraints set to top-bottom-leading-trailing, and add your subviews to it. Also, set the same constraints on the scrollView to the superView top-bottom-leading-trailing.
I believe the line of code below is the problem
scrollContentView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
You are setting your content view to the top of the view, when you should be setting it to the top of the scrollview.
I've just overcome a similar issue were I was setting the topAnchor of my first view to the safeAreaLayoutGuide.topAnchorof the scrollView. Everything laid out correctly but the constraint wouldn't show and therefore the entire content of the scrollView didn't move.
The problem is that you don't tell where the bottom of your content is. In other words you need some bottom constraints.
If you use...
scrollContentView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
...you need also to add a constraint to bind at least one view to the bottom of your UIScrollView like:
scrollContentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
... and also bind the last view in the scrollContentView to its bottomAnchor.
jobTypeDividerLine.bottomAnchor.constraint(equalTo: scrollContentView.bottomAnchor).isActive = true
This will sure fix your issue. Because this way the whole constraint sequence is linked from top to bottom.
Bottom line, the UIScrollView is not that smart that it determines its own bottom in every possible way. It is a kind of lazy. If you don't tell him enough it wouldn't simply scroll, while it is clear that your content disappears behind the bottom of your UIScrollView container.

Turning translucency off on navigation bar missalignes views

I have created layout with navigation bar and I have turned navigation bar translucent to no. I have added this code:
var overlay : UIView? // This should be a class variable
overlay = UIView(frame: view.frame)
overlay!.backgroundColor = UIColor.blackColor()
overlay!.alpha = 0.8
view.addSubview(overlay!)
if I understand it correctly this should create overlay over my view. But it gives me result of this:
So I think that this missalignes my view. Any idea how to fix this?
Its is happening because view origin changes if you set translucent
off. So Instead of using view.frame use view.bounds.
var overlay : UIView?
overlay = UIView(frame: view.bounds)
overlay!.backgroundColor = UIColor.blackColor()
overlay!.alpha = 0.8
view.addSubview(overlay!)
Replace your code as below.
overlay = UIView(frame: view.bounds)
overlay!.backgroundColor = UIColor.blackColor()
overlay!.alpha = 0.8
view.addSubview(overlay!)
Reason to use bounds instead of frame is,
you have turned of translucency. So your view will start from (0,64) instead of (0,0);
thats why its getting y=64 in frame value,
you can set y=0 or directly use view.bounds, in bounds it will give(x,y) = (0,0) and height and width as same as view.

Resources