UIKit Scrolling Debug
Recently, I am working with a friend to publish the app we worked on recently for my CodePath iOS course. We are refining a couple of the issues we are facing. The current issue I am facing is glitching/snapping scrolling. The author (by line) scrolls in sync with the blog image. But the blog content “snaps” toward the author.
In UIKit, the term “superview” refers to the parent view of a given view within the view hierarchy. Every view in UIKit has a superview except for the topmost view in the hierarchy, which is typically the UIWindow.
In UIKit and Xcode, the term “content view” typically refers to the primary view that holds the main content of a view controller or a custom view. It is the main container responsible for displaying and managing the visible content within a view.
Here are a few key points to understand about the content view in UIKit:
- View controller’s content view: In the context of a view controller, the content view refers to the view property of the view controller. It represents the top-level view managed by the view controller and is the container for all other subviews associated with that view controller’s content.
Most of our work will be in the Storyboard file AKA Main (Base)
. And in the right panel, specifically the Constraints tab (triangle ruler or a drafting triangle shape). So what is the correct term to describe [Trailing/Leading/Top/Bottom] Space to: [View]
under the Horizontal/Vertical section headings? Is it constraint?
In UIKit, Storyboard, and Xcode, the correct term to describe “top space to” and “bottom space to” is indeed “constraint.” Constraints define the layout and positioning of user interface elements within a view hierarchy. They establish relationships between different UI elements, specifying their position, size, and alignment relative to each other or to the containing view.
When you say “top space to” or “bottom space to,” you are referring to specific constraints that dictate the vertical spacing between UI elements. For example, “top space to” would mean creating a constraint that determines the distance between the top edge of one UI element and the top edge of another UI element or the top edge of the containing view. Similarly, “bottom space to” would define the vertical spacing between the bottom edge of one UI element and the bottom edge of another UI element or the bottom edge of the containing view.
By using constraints, you can create a responsive and adaptive user interface that adjusts properly to different screen sizes and orientations. Constraints help maintain consistent layouts and ensure that UI elements appear correctly across various devices.
How to make the height of a View
change dynamically based on the heights of the child elements? When you change something dynamically, it means that the change occurs while the system or process is running or in progress. Instead of making static or fixed modifications that remain unchanged until explicitly altered, dynamic changes happen in real-time and can adapt or respond to various conditions, inputs, or requirements.
There is a single Stack View
inside the Scroll View
. That Stack View
consists of 2 View
(s): one consisting of the image/title/date, and the other consisting of author/blog text (teal background for clarity).
In the screenshot above, we see the first View
has a Vertical constraint of Height Equals: 300
. However, we see there is extra whitespace below the date when the elements have a height that adds up to less than 300
.
So obviously, the answer is to remove that constraint, right? Well, try that and you’ll see that the first View
collapses into nothingness:
So if neither works, what should I do to present the first View
but dynamically change the height depending on the heights of the sub-elements?
According to my mentor, each of the children elements must have a clearly defined height so that the Superview
knows what height to assume.
While that is true, there was an additional prerequisite in order for the View
to dynamically fit the subviews based on their heights. So I had already defined a specific height for the image, and the text labels for title and date have an intrinsic height. I also had spacing between the image and the Stack View
holding the title/date labels i.e., Bottom Space to: Stack View Equals: 10
. So what was the missing ingredient?
TLDR, according to ios — Set UIView size to fit subViews — Stack Overflow, there is a quite simple way to set the parent’s height dynamically within storyboard. And that is to set a Top Space to: Image View Equals: 8
Vertical constraint. Basically, we set a distance of 8 between the top edge of the Image View
and the top edge of the containing view ( View
). The number 8 is arbitrary and can be adjusted smaller or larger depending on if you want a smaller or larger distance between the elements. Note: to create this constraint, press Ctrl
, then click and drag from Image View
to (parent) View
. Let go of the drag, and a menu should pop up. I clicked Top Space to Container
and that adds a Vertical constraint in the right-hand menu.
Second, set a bottom constraint between the View
and its last child view. So I repeat the same process as above, but from Stack View
(containing Blog Title
and Blog Date
) to (parent) View
.
Select Bottom Space to Container
from menu.
And that’s it! Quite simple in retrospect.
This was the PR fixing the Scrolling Glitch: Fix Scrolling glitch by MichaelDacanay · Pull Request #34 · MichaelDacanay/BlogSmart (github.com)
P.S.
Now for the long story… initially, I tried a programmatic approach where I created a Swift file for UIView
(not UIViewController
). The parent view ( View
) would calculate its height from the heights of each of the subviews in that Swift file. It would look something like this:
// HeaderView.swift
import UIKit
class HeaderView: UIView {
// Add your custom code here
@IBOutlet weak var parentView: UIView!
@IBOutlet weak var parentViewHeightConstraint: NSLayoutConstraint!
var childViews: [UIView] = []
override func viewDidLoad() {
super.viewDidLoad()
// Add child views from Interface Builder to the array
childViews = parentView.subviews
updateParentViewHeight()
}
func updateParentViewHeight() {
var totalHeight: CGFloat = 0.0
for childView in childViews {
totalHeight += childView.frame.size.height
}
parentViewHeightConstraint.constant = totalHeight
}
}
First of all, viewDidLoad()
is not defined on UIView
interface but UIViewController
. Second, for some reason I could not add this HeaderView.swift file as the custom class to (parent)View
.