How to add a UIView on top of MessageKit's messagesCollectionView - ios

I am trying to add a UIView on top of the messagesCollectionView, but the collectionview seems to take up the whole screen making the UIView I want to add unable to be seen. I am looking at their documentation here: https://github.com/MessageKit/MessageKit/blob/master/Sources/Layout/MessagesCollectionViewFlowLayout.swift
My thoughts are to change something in the Layout delegate but I am unsure what and where to change... any thoughts on this would be appreciated!
Thanks!

If you want to have some sort of floating view above the messagesCollectionView, then you can just add it as a subview to the MessagesViewControler's view, just make sure to do it after you've called super.viewDidLoad() because that's where MessageKit adds the collectionView as a subview so if you add it before then, then your view will be behind the collectionView and won't appear. To prevent the cells overlapping with your view, you can use messagesCollectionView.contentInset property to add padding to either the top or bottom if your view is floating there, so that the user can still scroll to all of the messages. Something like this:
override func viewDidLoad() {
super.viewDidLoad()
let subview = UIView()
view.addSubview(subview)
// Either add constraints or set the frame manually
// ...
// Set the contentInset if you want to prevent the messages from overlapping your view
messagesCollectionView.contentInset.top = 10 // For example, if your view was stickied to the top and was height 10px
}
Another route you could go is to have a parent view controller where you add the MessagesViewController as a child VC to the parent, and then size and layout the messagesCollectionView how you want. You can see an example of this in the MessageKit example app, in the MessageContainerController class: https://github.com/MessageKit/MessageKit/blob/master/Example/Sources/View%20Controllers/MessageContainerController.swift

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.

Have UIView extend to the bottom of UIScrollView

I put a UIScrollView occupies the whole area of my controller's view. Then I added a UIView (yellow color) on UIScrollView:
In my controller code, I have set the height of my scroll view:
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.contentSize.height = 1000
}
When I run it and scroll up on the screen, I see this:
How to make the yellow view component's bottom matches the bottom of scroll view?
From the information provided the only guess could be that you forgot to add layout constraints.
In case you did add them, please update the question showing your constraints.

UIScrollView for a smaller UIView inside main UIView

I'm having trouble getting this working, best explained what i am trying to do with this image of my storyboard.
The main goal is to make that container scrollable, with its size dependent on the view controller that gets added into it via code.
Here is how i have laid out my views.
Obviously my constraints are what is messing me up, but not exactly sure how to get them to behave as expected, I've tried pinning the container to the scroll view and then the scroll view to the main view. The screen appears how i want but it just doesn't scroll.
Any help is much appreciated!
It's impossible from your question & screenshots to determine what constraints you actually do have set up. And your question doesn't even particularly make it clear whether your constraints are behaving properly (aside from the scroll view not scrolling, which may or may not be related to constraints).
First, we need to make sure our constraints are hooked up correctly. The container view for which you're going to put a scroll view into should have four constraints. One for each side, left & right, pinning it to those edges. One for the bottom to pin it to the toolbar, and one for the top to pin it to your top views. It doesn't need any more constraints. Now, the toolbar at the bottom and your views at the top need to have their constraints set up so that they have a constant height per device/orientation, and does not care about the size of the container view. If your constraints are set up correctly, then resizing this view in your interface builder file should be changing the size of the container view. If this isn't happening, head back to the drawing board because something isn't quite right.
If you are confident your constraints are set up correctly, there are a few other things about the scrollview itself that could prevent scrolling.
First, check the most obvious. Is the scrolling enabled property set to true on your scroll view? If not... of course it's not going to scroll.
The other thing that could be happening is that your content view is not larger than the scroll view. Make sure that you've properly set up autolayout for the contents of the scroll view, otherwise the content view will not be larger than the scroll view, and no scrolling will be happening.
So for anyone wondering how to do this, i finally figured out how to do it after a lot of trial and error.
Basically what I did was remove the container view from the storyboard, set my constraints on the scroll view as i normally would:
and then added a container view as a subview of the scroll view in code:
var containerView : UIView!
var currentViewController: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
self.containerView = UIView()
self.scrollView.addSubview(self.containerView)
}
To swap between different view controllers using my UISegmentControl and have the scroll view scroll the content no matter how large the new view controller is i added the following code in my UISegmentControl functions
#IBAction func segControlValueChanged(sender: AnyObject) {
if let vc = viewControllerForSelectedSegmentIndex(sender.selectedSegmentIndex) {
self.currentViewController!.view.removeFromSuperview()
self.currentViewController!.removeFromParentViewController()
displayCurrentTab(sender.selectedSegmentIndex)
}
}
func displayCurrentTab(tabIndex: Int){
if let vc = viewControllerForSelectedSegmentIndex(tabIndex) {
self.addChildViewController(vc)
self.containerView.addSubview(vc.view)
vc.didMoveToParentViewController(self)
vc.view.frame.size = CGSize(width: self.view.frame.width, height: vc.view.frame.height) //had to add this because otherwise for some reason my new vc width would not increase or decrease to its parent view
self.currentViewController = vc
self.scrollView.contentSize = vc.view.frame.size
}
}
func viewControllerForSelectedSegmentIndex(index: Int) -> UIViewController {
//instantiate and return your view controller here
}
Some other notes:
I disabled "resize view from NIB" in storyboard for all my view controllers i plan to add to the container

Add a ScrollView to existing View

I'm developing a little app in Swift 2.0. I have a View with the following hierarchy:
Now, the elements placed in this view can't be displayed entirely in it, so I would like to use a ScrollView in order to be able to scroll all the content.
How I can embed all the content of my Viewto a new ScrollView? Can I do this programmatically by code?
UPDATE: There's an easier way to do this which I didn't know when I posted this answer
1) Go to the viewcontroller on the storyboard and click on something in it and press Command + A. This will select all the elements in the view.
2) Go to Editor -> Embed In -> Scroll View. This will embed all the labels, views, etc into a scrollView.
3) Select the scrollview that you just embedded. Press Control and drag it to the respective class file of the viewcontroller and create an outlet scrollView.
4) Add this to your viewDidLoad()
scrollView.contentSize = CGSizeMake(self.view.frame.width, self.view.frame.height+100)
Make sure the height for the contentSize is more than the size of your view.
ADDING SCROLLVIEW MANUALLY
1) Drag and drop a scrollview from the Object Library onto your viewcontroller in the storyboard and create an outlet to your program as 'scrollView'.
2) Click on your viewcontroller and go to the size inspector and note down the width and height.
3) Click on your scrollview and set the width and height the same as the viewcontroller. and set the X and Y values to 0
4) Click on the scrollView and drag it a little bit to the side
5) Press Command+A to select all the elements including scrollView. Press Command and click on the scrollView to deselect the ScrollView
6)You will have all the elements except the scrollView selected now. Now click and drag them into the scrollView.
7) Now click on the scrollView and set the X and Y values to 0 from the Size Inspector.
8) Add this to your viewDidLoad()
scrollView.contentSize = CGSizeMake(self.view.frame.width, self.view.frame.height+100)
Make sure the height for the contentSize is more than the size of your view.
That should create a scrollView for your view. Some of the elements might be in a slightly different position. You can easily fix them by moving them on your storyBoard.
It can be done even simpeler than ebby94's answer.
1) Go to the viewcontroller on the storyboard and click on something in it and press Command + A. This will select all the elements in the view.
2) Go to Editor -> Embed In -> Scroll View. This will embed all the labels, views, etc into a scrollView.
3) Set the constraints of the Scroll View to the View's edges.
And you're good to go! No need for an outlet.
If you are using Snapkit or creating programmatically.
class ScrollViewController: UIViewController {
lazy var contentViewSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 320) //Step One
lazy var scrollView : UIScrollView = {
let view = UIScrollView(frame : .zero)
view.frame = self.view.bounds
view.contentInsetAdjustmentBehavior = .never
view.contentSize = contentViewSize
view.backgroundColor = .white
return view
}()
lazy var containerView : UIView = {
let view = UIView()
view.frame.size = contentViewSize
view.backgroundColor = .white
return view
}()
override func viewDidLoad() {
self.view.addSubview(scrollView)
self.scrollView.addSubview(containerView)
//Now Set Add your Constraints in the container View.
}
}
Above accepted answer explanation is enough to achieve the scroll view but I would prefer to create my complete application programatically and I don't use storyboards in my project. This code is for the folks who don't prefer to use storyboards.
Explanation
Step One: Determine your content Size. Here I am taking Exact width and adding 320 more to the height of the screen.
Step Two: Create a scroll view and add desire behaviour of the scroll view. Now, the contentSize of the scroll view should be same as the contentSize you've created above at step one.
By Following Step one and Step Two. You Will be able to set a scroll view on the top of the view. But If you want to add a stretching behaviour then You should follow Step Three
Step Three: Create a container view of the same size of the contentView which you've calculated in step one and set it to the frame of the containerView. By doing this you'll be able to achieve stretching header and footer behaviour in your screen.
Please read make sure to add constraints in the same order as it is set.
Answer Edits are welcome.
I had the same issue. I needed to add scrollview to the existing view.But my main container view has a lots of view inside it. And they were connected to each other. So i was afraid. Finally i did it. the process given below.
Duplicate your View (root View). For this first select the root view then press Command + D
Now delete all the child view view inside the root view
Now add a scroll view to the root view and set constraint to 0,0,0,0
Now add the duplicate(That you duplicated) view to the scroll view and set constraints to 0,0,0,,0 also set the height that you want.
Set width of the duplicate view by equal width with the root view.
Now select the viewController, go the size inspector, select free form size. Then set the height that you entered with duplicate view.
You have almost done. Now you have to connect the child view with outlet or action that you gave in the viewController class.
Thats all.
Selecting all elements and embedding scroll view (editor->embed in->scrollView) works fine.
Adding constraint is much more easy by selecting the constraint warning (Add Missing Constraints).
It's simple. Command A and Command X to specific view controller in StoryBoard. After that take scroll view. On scroll view, just take one view with view controller view height and width equal to scroll View width. Again do Command V and rearrange the constraints. Your problem will be solved.

Frame change of table view cell's subview won't take place until view did appear

I want to change a UITableViewCell's subview when it is being setup in the cellForRowAtIndexPath with the following code:
cellSubView.center = CGPointZero
Printing out the frame's coordinates shows, that the frame updates successfully, however the view is still displayed in the position that was given in the interface builder.
Overriding the viewDidAppear function with the following code would resolve this issue:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.tableView.reloadData()
}
But this will result in blinking: the table view already did appear when I change one of its cells' subview's position.
How is it possible to change the frame before the view did appear?
If you used autoLayout check if it's creating the constraints automatically.
If yes, then you can change the constants of these constraints instead.
If you are not using autoLayout, call layoutIfNeeded and see if that changes anything.
My bet though is on autoLayout creating constraints automatically.

Resources