Is there a way to get a collectionViews frame from a collectionviewcell - ios

I want to use
-preferredLayoutAttributesFittingAttributes inside the UICollectionviewCell subclass we are writing.
Currently we already had a weak pointer to out parentViewController and I try to set the collectionViewcell frame width to be the same as parentViewcontroller.view.frame.size.width.
I don't want to have a pointer to the parentViewController from the CollectionViewCell subclass as having a reference to the parentviewcontroller is a bad idea.
My question is....
Is there a simple and ideal way to get the frame of collectionviewcell's super view from inside the collectionViewcell class?

If you want to be use the parent view then you don't need any reference. You can do like this:
CGFloat width = self.superview.frame.size.width;
And as #Ralfonso pointed out in the comments:
Also, if you want to get the superview size immediately after being added to a view hierarchy, override didMoveToSuperview: and access the superview property. If it's nil, the view has been removed from the superview. If non-nil, the view has been added to the superview's hierarchy.

Related

TableView empty inside ScrollView unless I add another UIView?

I have a ViewController with a ScrollView inside (pinned to edges, 4 constraints). Everything fine.
I then add a TableViewController as a ChildViewController to the ScrollView. The TableViewController is hardcoded to have 3 rows. The TableViewController's view is pinned to the full ScrollView.
#IBOutlet weak var SV: UIScrollView!
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let TVC = storyboard.instantiateViewController(withIdentifier: "TVC") as! TVC
addChildViewController(TVC)
SV.addSubview(TVC.view)
TVC.didMove(toParentViewController: self)
TVC.view.translatesAutoresizingMaskIntoConstraints = false
TVC.view.topAnchor.constraint(equalTo: SV.topAnchor).isActive = true
TVC.view.leadingAnchor.constraint(equalTo: SV.leadingAnchor, constant: 100).isActive = true
TVC.view.trailingAnchor.constraint(equalTo: SV.trailingAnchor, constant: -100).isActive = true
TVC.view.bottomAnchor.constraint(equalTo: SV.bottomAnchor).isActive = true
}
If I open debugger, I see that the ScrollView layout is ambiguous and that override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell never gets called for the table view controller (why ?).
Instead, if I simply add a label within the storyboard or any other view, inside the scrollview, and add 4 constraints to it, then the TableView will be shown and override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell is called as expected.
What's up with this ? ScrollView shouldn't be ambiguous, because the TableView should calculate it's own intrinsic content size ? What's wrong with the tableview that it never populates itself if it's alone inside the ScrollView ?
I'm adding the whole sampleproject. If you run it, the result is expected, the tableview is shown. If you go to storyboard and remove the label from the ScrollView, then the TableView won't be populated at all.
I don't know how I can directly add the sampleproject here, so I uploaded it. Just run it, then remove the only label from storyboard and re-run it, no more table view.
https://wetransfer.com/downloads/e0a75a995992cdc2be9120224515549920181003074547/1083bc9a3cb34fe9b1ab8e513b1520f020181003074547/e68826
PS: ignore other things, like child view controlers in there. My only question is why the tableview is shown when the label exists inside the same ScrollView and not shown when the label is removed.
First of all, the constraints applied to the views inside a ScrollView are a little bit, let's say, special. They are not linked to the ScrollView's frame, they are sort of linked to the ScrollView's "contentSize". Normally, if you would have a View inside another view, and the inside view is pinned in all directions to the superView, it will either stretch to match the parent size (if the parent constraints are well defined to a different parent), or the parent will "wrap" the child size, if the parent constraints are not defined relative to another parent.
Now the ScrollView behaves a little bit different in this case, the ScrollView size will never "wrap" the child. The child constraints are added just to tell the ScrollView, what's the scroll area (contentSize).
Moreover, in your case the UITableView, you can think of it like a "special" ScrollView, can't provide enough information for the ScrollView in order to compute the scrollableContent. If you pin the tableView in all directions, it will just tell the TableView, "align with the parent in all directions" but the scroll view will not know "how much can I scroll".
So because of the above problem, you usually add a UIView inside the ScrollView to act as a "container" and depending on the scrollDirection, you set the view width/height to be equal to the ScrollView or to the parent of the scrollView (again depending on the use case). Then all the content that you want to be inside the scrollView it's added inside the UIView. Also the content is pinned in such way, that it won't allow "compression" to force the parent view (aka container) to stretch to "wrap" the whole content, in this way, the scroll will know how much is the scroll area.
ScrollView shouldn't be ambiguous, because the TableView should calculate it's own intrinsic content size ?
This one is wrong, the intrinsic content size, defines the minimum size that the view needs in order to be drawn. Now think about this, how much space does a tableView needs in order to be drawn, if no other special implementation is added, the answer is 0. The tableView can have a width/height of 0, there's no constraints to prevent this.
Now to wrap it up, your implementation is a little bit wrong, to fix it, I suggest to have a close look over one of the many tutorials that shows how to implement a UIScrollView with auto-layout.
You'll see that most of the implementations will:
add a UIView inside the UIScrollView (aka "containerView)
then the view will be pinned in all directions to the UIScrollView
then either the width or the height of the container will be set to be equal to the scrollView or the parent of scroll view
then content will be added inside the "containerView"
then constraints will be applied to the content in such way that the container will have to wrap around the content.

Combine UIView and UITableView

Hi Is it possible to combine to combine a UIView with a UITableView and reference it as a single object? I currently have a both combined as a Stack, but I feel there is a better way to do it. Thanks
They are two different objects, one property cannot reference two different views. You can however use an IBOutlet for both views in your ViewController, or within any ancestor view.
class MyViewController : UIViewController {
#IBOutlet var tableView : UITableView!
}
Then, you can just drag a reference outlet for that specific view from Storyboard to the view controller. For information on that:
https://developer.apple.com/library/ios/recipes/xcode_help-IB_connections/chapters/CreatingOutlet.html
Note that I did not add a property for your UIView because I assumed it is already the main view of the controller. Similarly, you can subclass your main view, add an outlet for the table view, and use that as the reference outlet.
In my opinion, there are two way to resolve your require:
Option 1. use table view header view. I suppose you want to combine a view named topView with a table view. call tableView.tableHeaderView = topView
Option 2. add topView to table view as a subview, then adjust contentInset of table view. Just like:
UIEdgeInsets tableViewInsets = self.tableView.contentInset;
tableViewInsets.top = topView.frame.size.height;
self.tableView.contentInset = tableViewInsets;

Swift mimic animated: animation

I am using self.navigationController?.setNavigationBarHidden(true, animated: true)
I wonder where I can see how that animation is built in code?
I want to mimic its behavior with another custom UIView
The animation is just a slide up, so an animation is created that changes the y-origin of the view being animated.
If using auto layout, you should have a top constraint that specifies that the view's top y position is equal to the superview's top y position. You can reference these NSLayoutConstraints using #IBOutlet like you can with other storyboard elements, so in your view controller:
class MyViewController {
#IBOutlet var viewToAnimate: UIView!
#IBOutlet var topConstraint: NSLayoutConstraint!
}
You will notice that NSLayoutConstraint has a constant property, which just specifies a value to add to the second constraint attribute when calculating the resulting layout frames using that constraint. So in the case of the topConstraint, you want to add -viewToAnimate.bounds.height*, so that the bottom of the view sits just out of sight at the top of the superview.
You animate changes using the UIView class method animateWithDuration(_:animations:) - any animatable UIView properties that are changed inside the animation closure will be animated over the specified duration. But when you change your constraint's constant property, the view properties aren't changed until another layout pass is performed on the view. You can invalidate the layout, and let the layout happen in the next pass implicitly with view.setNeedsLayout(), but we need the layout pass to happen inside the view's animation block for the animation to work. So instead, you can use layoutIfNeeded() to force the subviews to layout immediately.
Put together, your animate method might look something like this:
class MyViewController {
// ...
func animateViewUp() {
UIView.animateWithDuration(0.3) {
self.topConstraint.constant = -self.viewToAnimate.bounds.height
self.view.layoutIfNeeded()
}
}
}
In reality, you would likely specify a method that allows you to toggle between a shown/hidden state, but the above code should get you started.
*N.B. Check which way around the constraint is specified! If the constraint is specified in terms of your superview, then a negative constant will move the subview downwards!
Short answer: You can't. The source for Apple's frameworks is not provided. However, most of their animations are built on top of the same Core Animation framework that us third party developers use.
What is the effect this creates? I'm not sure I've ever animated away the navigation bar on a view controller that's in place. Can you post a GIF of the animation? I can probably give you an idea of how it's done if I know what it looks like.

Get view's frame in UIView that is not superview

I would think this is a simple question, but I cannot seem to find a way to do this.
I have a view that is a subview of my uiviewcontroller. Within that view, I have another view. To clarify, this is the architecture:
>UIViewController.view
--->Subview A
------->Subview B
I want to get the frame of subview b within uiviewcontroller.view.
Is this possible? subviewB.frame gives me the frame of the view within Subview A.
Use the UIView convertRect:toView: method:
CGRect frameRelativeToViewControllerView = [subviewB convertRect:subviewB.bounds toView:viewController.view];

How to set a subview's frame

I have a view hierarchy under a UIViewController that consists of a main view and a few subviews of the main view, all defined in a storyboard, and all connected using IBOutlet.
I would like to set the position and size of the subviews relative to the main view.
I've tried setting them in viewDidLoad and viewWillAppear. Neither work, and when called, the main view has valid frame values but the subviews all have frames of (0,0,0,0).
If I do nothing, the subviews appear exactly where positioned on the storyboard. I need to make the following code work, but I don't know where to put it.
CGRect newFrame = CGRectMake(10,10,50,50);
[subView setFrame:newFrame];
Another way to ask this question:
Where and when do the frames of storyboard subviews get set?
Or, should I just create all the subviews with code?
when using autolayout you can set frame of the views (if the frame is calculated from super view's frame) in viewDidLayoutSubviews method or the other way I use is calling this method [view setTranslatesAutoresizingMaskIntoConstraints:YES]; and then [view setFrame:...];. Hope this helps.
The position and size of subviews can be set when the UIViewController calls viewDidAppear but that causes the presentation to jump. Setting the position and size of a UITableView in viewDid/WillLayoutSubviews does not work.

Resources