Unable to update with of a UIVIew | iOS Swift - ios

I am following ios11-app-development-bootcamp by Angela. I am having problem to update width of a UIView. Here is the code I am using
#IBOutlet var progressBar: UIView!
progressBar.frame.size.width = (view.frame.size.width / 13) * CGFloat(questionNumber + 1)
The width is setting correctly but the UI is not updating. I have searched and found below solution.
#IBOutlet weak var progressBarWidthConstraint: NSLayoutConstraint!
progressBarWidthConstraint.constant = (view.frame.size.width / 13) * CGFloat(questionNumber + 1)
Why is below code working and above code not working. I am watching this lecture link
Thanks in advance.

In viewDidload it will take an initial width of the view given in the storyboard.
we can set the width in viewDidAppear
or
Add view.setNeedsLayout() Or view.layoutIfNeeded() in viewDidLoad after setting the width.

If you want to redraw your view, you just need to call layoutIfNeeded
Use this method to force the view to update its layout immediately. When using Auto Layout, the layout engine updates the position of views as needed to satisfy changes in constraints. Using the view that receives the message as the root view, this method lays out the view subtree starting at the root. If no layout updates are pending, this method exits without modifying the layout or calling any layout-related callbacks.

Related

how to change view controller width size programatically?

I am trying to make a viewcontroller manually without using pod following this tutorial https://www.youtube.com/watch?v=Hl_Re_KLhcY&t=141s
but the sideMenuVC (the green one) has width that set accurately, I mean, the sideMenuVC should be 80% of the size of MainMenuVC. so if I change from iPhone 5s to iPhoneX, the autolayout still look perfect. But this is what I get
the sideMenuVC should be 80% only, but it looks full size on iPhone 5s
in sideMenuVC viewDidLoad I want to set something like this
class SideMenuVC: UIViewController {
#IBOutlet var sideMenu: UIView!
override func viewDidLoad() {
super.viewDidLoad()
sideMenu.frame.width = self.view.frame.width / 2
}
}
but it doesn't work since it is get only property
so I want to change the width in here
What you would optimally want to achieve your desired output is keep the view controller's width as is, but add a normal view inside with the following constraints
Leading to superview with a constant value of 0
Top to superview with a constant value of 0
Bottom to superview with a constant value of 0
Width equal to superview with a multiplier value of 4:5
You also need to set the background color of the UIViewController's default view property to .clear.
Finally, set the presentation style of that newly created UIViewController to Over Current Context.
When presenting a view controller using the UIModalPresentationFullScreen style, UIKit normally removes the views of the underlying view controller after the transition animations finish. You can prevent the removal of those views by specifying the UIModalPresentationOverCurrentContext style instead. You might use that style when the presented view controller has transparent areas that let underlying content show through.

PDFKit - PDFThumbnailView skips a page instead of showing continuous pages - swift

I am able to present the PDFThumnailView of my pdf document. The problem is it is skipping pages. It shows pages 1,3,5 etc...and not displaying the pages in between. My code is below.
#IBOutlet weak var pdfView: PDFView!
#IBOutlet weak var pdfThumbnailView: PDFThumbnailView!
func setupThumbnailView() {
pdfThumbnailView.pdfView = pdfView
pdfThumbnailView.thumbnailSize = CGSize.init(width: thumbnailDimension, height: thumbnailDimension)
pdfThumbnailView.backgroundColor = sidebarBackgroundColor
}
I might be wrong but I think this is the way that PDFThumbnailView works. It fits as many thumbnails as it can within the View width by not displaying some intermediate thumbnails. It does show them if you touch over the thumbnails. Make the width bigger or the thumbnails smaller and it shows more.
You have to make pdfThumbnailView bigger. However then it can be too big to fit on the screen, so let’s put it in a scroll view.
First make change the constraints on the pdfThumbnailView to make it wide enough to accomodate all the pages.
NSLayoutConstraint.activate([
pdfThumbnailView.heightAnchor.constraint(equalToConstant: CGFloat(thumbnailSize)),
pdfThumbnailView.widthAnchor.constraint(equalToConstant: CGFloat(pdfDocument.pageCount*thumbnailSize))
])
Next create the scroll view and add the thumbnail view as its only subview.
var pdfThumbnailScrollView = UIScrollView()
pdfThumbnailScrollView.translatesAutoresizingMaskIntoConstraints = false
pdfThumbnailScrollView.addSubview(pdfThumbnailView)
Finally add some constraints so the scroll view and the thumbnail view know how to lay themselves out. The scroll view only has one subview, so let's constrain it to fit.
NSLayoutConstraint.activate([
pdfThumbnailView.leadingAnchor.constraint(equalTo: pdfThumbnailScrollView.leadingAnchor),
pdfThumbnailView.trailingAnchor.constraint(equalTo: pdfThumbnailScrollView.trailingAnchor),
pdfThumbnailView.topAnchor.constraint(equalTo: pdfThumbnailScrollView.topAnchor),
pdfThumbnailView.bottomAnchor.constraint(equalTo: pdfThumbnailScrollView.bottomAnchor)
])
You may follow this tutorial more details.

Xcode/IB - Autolayout, multiple Containers View and Scroll View

I am trying to build an interface with the IB under Xcode with some Containers View.
I will try to do my best to explain the problem:
I have a main scene which contains two controllers. The first one, at the top of the scene, contains a "Last Post View", which retrieves the last post from a Wordpress website and displays the cover image, the post's date and the title.
The second one contains a Collection View which leads to other views.
Functionally and independently, everything seems to work fine. The problem is that I can not figure how to make work this "stack" with autolayout and fit on portrait and landscape modes and different devices.
Here is my Storyboard
The Home Controller's constraints
The Last Post View's constraints
The Collection View's constraints
..and finally, what I get
After hours of searching and attempts, I found that the Scroll View, contained in my Home Controller, must have only one direct child. But I don't know how to put the different constraints. Plus, I always get the message: Scrollable content size is ambiguous for "Scroll View".
Another problem that I have, is when I am in landscape mode, I can't scroll the "whole view". At best, I can scroll the Collection View only (when I can display it) but not the entire screen.
(Maybe it can help if I said that I am using Swift 2)
Does anyone have a suggestion? It will be much appreciated!
Many thanks!
EDIT 1
I tried to apply the Xingou's solution and I think I am quite close the goal but I obviously miss something.
Here is my HomeViewController
class HomeViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var containerViewHeightConstrait: NSLayoutConstraint!
#IBOutlet weak var lastPostContainerView: UIView!
#IBOutlet weak var scrollContainerView: UIView!
#IBOutlet weak var mainCollectionContainerView: UIView!
/*************************/
/***** VIEW DID LOAD *****/
/*************************/
override func viewDidLoad() {
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false
self.lastPostContainerView.setNeedsDisplay()
self.mainCollectionContainerView.setNeedsDisplay()
self.containerViewHeightConstrait.constant = UIScreen.mainScreen().bounds.height - 64
//END viewDidLoad
}
...
/********************************/
/***** SET CONTAINER HEIGHT *****/
/********************************/
func needSetContainerHeight( height: CGFloat ) {
self.containerViewHeightConstrait.constant = height + lastPostContainerView.bounds.height + 200
self.scrollView.contentSize.height = height + lastPostContainerView.bounds.height + 200
print( "View Height Constrait \( self.containerViewHeightConstrait.constant )" )
print( "Scroll View Height \( self.scrollView.contentSize.height )" )
//END needSetContainerHeight
}
...
...and my MainCollectionController
class MainCollectionViewController: UICollectionViewController {
/*************************/
/***** VIEW DID LOAD *****/
/*************************/
override func viewDidLoad() {
super.viewDidLoad()
collectionView!.autoresizingMask = [ .FlexibleWidth ]
//END viewDidLoad
}
/****************************************/
/****************************************/
/***************************/
/***** VIEW DID APPEAR *****/
/***************************/
override func viewDidAppear( animated: Bool ) {
super.viewDidAppear( animated )
( self.parentViewController as! HomeViewController ).needSetContainerHeight( self.collectionView!.contentSize.height )
//END viewDidAppear
}
...
If I well understood what was proposed, here is how constraints should look like :
(Main) View
Scroll View
Subviews Container
Last Post Container
Collection Container
List of all constraints
... and what I get
Portrait
Landscape
I made a few tests with different extra constraints and I found out that I had to tell the Scroll View to fill its whole parent to display something on the screen (otherwise, I just get the red background).
Another thing is if I add a constraint for the space between Last Post Container and Collection Container, things are "well" positioned but I cannot click on the collection's cells anymore. But, if I don't add this constraint, things are more messy (for example, the collection view overlaps the post view), but I am able to click on the cells.
Unfortunately, the screen seems to be cropped and there are some differences when I rotate the screen. But I think I have to recompute the heights when the device is rotated (am I right?). The margin at the top of screen still here, but not always: it depends in which mode I started the app and how many rotation I do.
Another thing I forgot to mention, is the last post is asynchronously retrieved. So, the cover image, which has a variable size, is displayed after the main screen is displayed. I moved this in the AppDelegate - didFinishLaunchingWithOptions and stocked the post in the Notification Center but unfortunately, there's still some delay.
Any suggestions? :)
SOLUTION
Like Xingou said, it is far easier to use the header section of the collection view (Accessories / Section Header).
you need to solve the following thing:
in you home sense, viewdidload , add the following code:
self.automaticallyAdjustsScrollViewInsets = false
this can make the red area between navagationbar and you picture disappear.
fixed the Scrollable content size is ambiguous for "Scroll View":
this is because the scrollview did not know the content size it would show。you need reset the constrain of the view( the two container view's super view ),i will call it scrollcontainerview:
scrollcontainerview.leading = scrollview.leading
scrollcontainerview.trailing = scrollview.trailing
scrollcontainerview.top = scrollview.top
scrollcontainerview.bottom = scrollview.bottom
scrollcontainerview.width = self.view.width
scrollcontainerview.height = 603 //we will chang this value through code
now the "the Scrollable content size is ambiguous for "Scroll View"" error should be disappeared.
change the scrollcontainerview.height in code :
in you home scene, drag the scrollcontainerview.height constraint into you view controller,and chang it to fit the screen:
#IBOutlet weak var contianerViewHeightConstrait:
NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false
//because we set the height at const 603,but height is different on different device,so we need chang it to the correct value
contianerViewHeightConstrait.constant = UIScreen.mainScreen().bounds.height - 64
}
and now you can see the two container view fill the screen。
need scroll the whole content, not only the collection view:
to solve this problem ,you need assign a correct height to the scrollview.contentsize.height .
in you collection view controller:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
(self.parentViewController as! HomeScenceViewController).needSetContainerHeight(self.collectionView.contentSize.height)
}
then add a method in HomeScenceViewController
// set the containerview and scrollview height
func needSetContainerHeight(height:CGFloat){
//the 200 is your first container view's height
contianerViewHeightConstrait.constant = height + 200
scrollview.contentSize.height = height + 200
}
and now you should can scroll the whole content

Position UIView programatically not working - stuck to storyboard positioning

I have the following view with the following constraints:
#IBOutlet weak var square1ViewOutlet: UIView!
Which is inserted inside the following view using the storyboard:
#IBOutlet weak var holderView: UIView!
My problem is that I am not being able to override the positioning of square1ViewOutlet established by the storyboard. The following code does not have any effect. I've tried some variations as bellow, but nothing works and the view is really stuck to previous storyboard constraints. I am calling this with my viewDidLoad method.
square1ViewOutlet.frame.origin.y = self.holderView.frame.origin.y + 20
square1ViewOutlet.frame.origin.x = self.holderView.frame.origin.x + 20
square1ViewOutlet.frame = CGRectOffset(CGRect(origin: square1ViewOutlet.frame.origin, size: square1ViewOutlet.frame.size), 20, 20)
square1ViewOutlet.center = CGPointMake(150, 150)
square1ViewOutlet.frame = CGRectMake( 100, 200, square1ViewOutlet.frame.size.width, square1ViewOutlet.frame.size.height )
Any idea what am I doing wrong here?
When you use autolayout and you try to change frame dimensions or positions like your code, instead to correct the correct constraints involved, you can disable you constraints effect causing warnings and unexpected view dimensions and movements.
The correct way to do it is to link the constraints as IBOutlets in your code:
#IBOutlet weak var customViewTopConstraint: NSLayoutConstraint
#IBOutlet weak var customViewHeightConstraint: NSLayoutConstraint
#IBOutlet weak var customViewLeadingConstraint: NSLayoutConstraint
#IBOutlet weak var customViewWidthConstraint: NSLayoutConstraint
And work with these properties changing it's constant value (.constant).
These rules are valid for all the code where you have to change frame dimensions, so check all your code and change it to a constraints work.
Can you please try that code inside your viewDidAppear() method. please go to the link below iOS UIViewController Lifecycle.
Since you have placed your code inside viewDidLoad method, that may be the reason your frames are not applied.
If this not worked please try layoutIfNeeded method for your view after the frames are set. layoutIfNeeded forces the receiver to layout its subviews immediately if required.
By this way you can set your frames for your views. But doing this way will alter your constraints that you had set in your xib file. If you really want to change your view frames, then best way will be create outlet for your constraints and change the values for that constraints. Hope this will help :)

Get View height based on margin constraints after runtime Swift

I have a circle in the centre of a screen with a margin constraint of 50 on either end. Hence, the width of the circle is dependent on the screen size.
So, what I tried was this:
Approach 1
I set up the margins in the storyboard to define the circle width (50 on left and right)
Then I used the following code:
#IBOutlet weak var helpButHeight: NSLayoutConstraint!
#IBOutlet weak var helpBut: UIButton!
ViewDidLoad:
helpButHeight.constant = helpBut.frame.size.width
This didn't work.
Since the screen width is 400, and the margin is 50 on either end, then helpBut.frame.size.width should have given me 300.
Instead it gave me 46.
Approach 2
This was my work-around:
ViewDidLoad:
let screenSize: CGRect = UIScreen.mainScreen().bounds
helpButHeight.constant = screenSize.width - 100
because 100 = 50 + 50, the two margins.
Works fine !
Question
Why did I have to do this? Why did the first approach not work? Why 46 and not 300?
The reason is that constraints haven't kicked in, in the viewDidLoad function. The lifecycle looks something like
viewDidLoad -- Constraints haven't set
viewWillAppear -- Constraints haven't set
viewWillLayoutSubviews -- Constraints are setting
viewDidLayoutSubviews -- Constraints are set
viewDidAppear -- Constraints are set
If you want any view to be in center just put the horizontal/vertical center constraint...No code required.. If you want to have padding just put the left and right constraints...Just to remind you don't use both of them together...It'll break...

Resources