Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent offscreen rendering #127

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 56 additions & 35 deletions PanModal/Controller/PanModalPresentationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,13 @@ open class PanModalPresentationController: UIPresentationController {
private lazy var dragIndicatorView: UIView = {
let view = UIView()
view.backgroundColor = presentable?.dragIndicatorBackgroundColor
var alpha: CGFloat = 1.0
view.backgroundColor?.getRed(nil, green: nil, blue: nil, alpha: &alpha)
view.isOpaque = alpha == 1.0
view.layer.cornerRadius = Constants.dragIndicatorSize.height / 2.0
if #available(iOS 13.0, *) {
view.layer.cornerCurve = .continuous

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a pill shape, I think we want the standard circular corners instead of continuous

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense? Both are visually similar but the continuous one does the same as the current mask solution. Did you test my code?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're conflating switching to cornerRadius (which the dragIndicatorView already does) and specifying a continuous vs circular curve. A continuous curve is nice aesthetically when a corner is being rounded by itself. It creates a subtle "squircle" shape, rather than a simple circular radius that has a jarring transition to the flat part of the shape. This works great for something like the sheet view itself, so I support using continuous in that code.

However, in this drag view the two corners meet, and when you use the continuous style, this leads to point edges. The circular style has the desired effect, since it's two quarter circles that match perfectly. Here's a blown-up demonstration of the two effects when the radius is equal to the height / 2:

Simulator Screen Shot - iPhone 12 Pro Max - 2021-01-29 at 12 12 18

I hope that make sense!

Copy link
Author

@ppamorim ppamorim Feb 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am pretty sure that the continous solution is still idea for this project. The roundness of the curve is not the main point and the current solution already uses the continous solution. The rounded UIBezierPath does this automatically.

}
return view
}()

Expand Down Expand Up @@ -188,6 +194,7 @@ open class PanModalPresentationController: UIPresentationController {

coordinator.animate(alongsideTransition: { [weak self] _ in
self?.backgroundView.dimState = .max
self?.dragIndicatorView.layoutIfNeeded()
self?.presentedViewController.setNeedsStatusBarAppearanceUpdate()
})
}
Expand Down Expand Up @@ -347,14 +354,14 @@ private extension PanModalPresentationController {
containerView.addSubview(presentedView)
containerView.addGestureRecognizer(panGestureRecognizer)

if presentable.showDragIndicator {
addDragIndicatorView(to: presentedView)
}

if presentable.shouldRoundTopCorners {
addRoundedCorners(to: presentedView)
}

if presentable.showDragIndicator {
addDragIndicatorView(to: containerView, on: presentedView)
}

setNeedsLayoutUpdate()
adjustPanContainerBackgroundColor()
}
Expand All @@ -376,6 +383,7 @@ private extension PanModalPresentationController {
// (rotations & size changes cause positioning to be out of sync)
let yPosition = panFrame.origin.y - panFrame.height + frame.height
presentedView.frame.origin.y = max(yPosition, anchoredYPosition)
dragIndicatorView.frame.origin.y = presentedView.frame.origin.y - Constants.indicatorYOffset
}
panContainerView.frame.origin.x = frame.origin.x
presentedViewController.view.frame = CGRect(origin: .zero, size: adjustedSize)
Expand All @@ -396,8 +404,8 @@ private extension PanModalPresentationController {
& configures its layout constraints.
*/
func layoutBackgroundView(in containerView: UIView) {
containerView.addSubview(backgroundView)
backgroundView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(backgroundView)
backgroundView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
backgroundView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
backgroundView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
Expand All @@ -408,10 +416,10 @@ private extension PanModalPresentationController {
Adds the drag indicator view to the view hierarchy
& configures its layout constraints.
*/
func addDragIndicatorView(to view: UIView) {
view.addSubview(dragIndicatorView)
func addDragIndicatorView(to view: UIView, on pinEdgeView: UIView) {
dragIndicatorView.translatesAutoresizingMaskIntoConstraints = false
dragIndicatorView.bottomAnchor.constraint(equalTo: view.topAnchor, constant: -Constants.indicatorYOffset).isActive = true
view.addSubview(dragIndicatorView)
dragIndicatorView.bottomAnchor.constraint(equalTo: pinEdgeView.topAnchor, constant: -Constants.indicatorYOffset).isActive = true
dragIndicatorView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
dragIndicatorView.widthAnchor.constraint(equalToConstant: Constants.dragIndicatorSize.width).isActive = true
dragIndicatorView.heightAnchor.constraint(equalToConstant: Constants.dragIndicatorSize.height).isActive = true
Expand Down Expand Up @@ -641,7 +649,7 @@ private extension PanModalPresentationController {

func snap(toYPosition yPos: CGFloat) {
PanModalAnimator.animate({ [weak self] in
self?.adjust(toYPosition: yPos)
self?.adjust(toYPosition: yPos, updateDragIndicatorView: true)
self?.isPresentedViewAnimating = true
}, config: presentable) { [weak self] didComplete in
self?.isPresentedViewAnimating = !didComplete
Expand All @@ -651,8 +659,11 @@ private extension PanModalPresentationController {
/**
Sets the y position of the presentedView & adjusts the backgroundView.
*/
func adjust(toYPosition yPos: CGFloat) {
func adjust(toYPosition yPos: CGFloat, updateDragIndicatorView: Bool = false) {
presentedView.frame.origin.y = max(yPos, anchoredYPosition)
if updateDragIndicatorView {
dragIndicatorView.frame.origin.y = presentedView.frame.origin.y - Constants.indicatorYOffset
}

guard presentedView.frame.origin.y > shortFormYPosition else {
backgroundView.dimState = .max
Expand Down Expand Up @@ -803,6 +814,7 @@ private extension PanModalPresentationController {
as if we're transferring the scrollView drag momentum to the entire view
*/
presentedView.frame.origin.y = longFormYPosition - yOffset
// dragIndicatorView.frame.origin.y = presentedView.frame.origin.y - Constants.indicatorYOffset
} else {
scrollViewYOffset = 0
snap(toYPosition: longFormYPosition)
Expand Down Expand Up @@ -843,39 +855,48 @@ private extension PanModalPresentationController {
*/
func addRoundedCorners(to view: UIView) {
let radius = presentable?.cornerRadius ?? 0
let path = UIBezierPath(roundedRect: view.bounds,
byRoundingCorners: [.topLeft, .topRight],
cornerRadii: CGSize(width: radius, height: radius))

// Draw around the drag indicator view, if displayed
if presentable?.showDragIndicator == true {
let indicatorLeftEdgeXPos = view.bounds.width/2.0 - Constants.dragIndicatorSize.width/2.0
drawAroundDragIndicator(currentPath: path, indicatorLeftEdgeXPos: indicatorLeftEdgeXPos)

if radius == 0 {
//In case removing the radius
view.layer.cornerRadius = 0
view.layer.masksToBounds = false
return
}

// Set path as a mask to display optional drag indicator view & rounded corners
let mask = CAShapeLayer()
mask.path = path.cgPath
view.layer.mask = mask
let maskedCorners: CACornerMask = CACornerMask(rawValue: createMask(corners: [.topLeft, .topRight]))

view.layer.cornerRadius = radius
view.layer.masksToBounds = true

if #available(iOS 11.0, *) {
view.layer.maskedCorners = maskedCorners
if #available(iOS 13.0, *) {
view.layer.cornerCurve = .continuous
}
}

// Improve performance by rasterizing the layer
view.layer.shouldRasterize = true
view.layer.rasterizationScale = UIScreen.main.scale
}

/**
Draws a path around the drag indicator view
*/
func drawAroundDragIndicator(currentPath path: UIBezierPath, indicatorLeftEdgeXPos: CGFloat) {
enum Corner: Int {
case bottomRight = 0,
topRight,
bottomLeft,
topLeft
}

let totalIndicatorOffset = Constants.indicatorYOffset + Constants.dragIndicatorSize.height
private func parseCorner(corner: Corner) -> CACornerMask.Element {
let corners: [CACornerMask.Element] = [
.layerMaxXMaxYCorner,
.layerMaxXMinYCorner,
.layerMinXMaxYCorner,
.layerMinXMinYCorner]
return corners[corner.rawValue]
}

// Draw around drag indicator starting from the left
path.addLine(to: CGPoint(x: indicatorLeftEdgeXPos, y: path.currentPoint.y))
path.addLine(to: CGPoint(x: path.currentPoint.x, y: path.currentPoint.y - totalIndicatorOffset))
path.addLine(to: CGPoint(x: path.currentPoint.x + Constants.dragIndicatorSize.width, y: path.currentPoint.y))
path.addLine(to: CGPoint(x: path.currentPoint.x, y: path.currentPoint.y + totalIndicatorOffset))
private func createMask(corners: [Corner]) -> UInt {
corners.reduce(0, { (a, b) -> UInt in a + parseCorner(corner: b).rawValue })
}

}

// MARK: - Helper Extensions
Expand Down
3 changes: 3 additions & 0 deletions PanModal/View/PanContainerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class PanContainerView: UIView {

init(presentedView: UIView, frame: CGRect) {
super.init(frame: frame)
var alpha: CGFloat = 1.0
presentedView.backgroundColor?.getRed(nil, green: nil, blue: nil, alpha: &alpha)
isOpaque = alpha == 1.0
addSubview(presentedView)
}

Expand Down
2 changes: 2 additions & 0 deletions PanModalDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Tests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
LD_RUNPATH_SEARCH_PATHS = (
Expand All @@ -690,6 +691,7 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Tests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down
7 changes: 7 additions & 0 deletions Sample/View Controllers/Basic/BasicViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ class BasicViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
view.isOpaque = true
view.backgroundColor = #colorLiteral(red: 0.1019607843, green: 0.1137254902, blue: 0.1294117647, alpha: 1)
let label = UILabel(frame: view.frame)
label.isOpaque = true
label.text = "Text"
label.textColor = UIColor.white
label.textAlignment = .center
view.addSubview(label)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ private class FullScreenViewController: UIViewController {

let textLabel: UILabel = {
let label = UILabel()
label.isOpaque = true
label.text = "Drag downwards to dismiss"
label.font = UIFont(name: "Lato-Bold", size: 17)
label.translatesAutoresizingMaskIntoConstraints = false
Expand All @@ -60,6 +61,7 @@ private class FullScreenViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "Full Screen"
view.isOpaque = true
view.backgroundColor = .white
setupConstraints()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ class UserGroupHeaderView: UIView {

let titleLabel: UILabel = {
let label = UILabel()
label.isOpaque = true
label.font = UIFont(name: "Lato-Bold", size: 17.0)
label.textColor = #colorLiteral(red: 0.8196078431, green: 0.8235294118, blue: 0.8274509804, alpha: 1)
return label
}()

let subtitleLabel: UILabel = {
let label = UILabel()
label.isOpaque = true
label.numberOfLines = 2
label.textColor = #colorLiteral(red: 0.7019607843, green: 0.7058823529, blue: 0.7137254902, alpha: 1)
label.font = UIFont(name: "Lato-Regular", size: 13.0)
Expand All @@ -33,6 +35,7 @@ class UserGroupHeaderView: UIView {

lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
stackView.isOpaque = true
stackView.axis = .vertical
stackView.alignment = .leading
stackView.spacing = 4.0
Expand All @@ -42,6 +45,7 @@ class UserGroupHeaderView: UIView {

let seperatorView: UIView = {
let view = UIView()
view.isOpaque = true
view.backgroundColor = #colorLiteral(red: 0.8196078431, green: 0.8235294118, blue: 0.8274509804, alpha: 1).withAlphaComponent(0.11)
view.translatesAutoresizingMaskIntoConstraints = false
return view
Expand All @@ -53,6 +57,7 @@ class UserGroupHeaderView: UIView {
super.init(frame: frame)

backgroundColor = #colorLiteral(red: 0.1019607843, green: 0.1137254902, blue: 0.1294117647, alpha: 1)
isOpaque = true

addSubview(stackView)
addSubview(seperatorView)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ class UserGroupMemberCell: UITableViewCell {

let avatarView: UIView = {
let view = UIView()
view.isOpaque = true
view.layer.cornerRadius = 8.0
return view
}()

let nameLabel: UILabel = {
let label = UILabel()
label.isOpaque = true
label.textColor = #colorLiteral(red: 0.8196078431, green: 0.8235294118, blue: 0.8274509804, alpha: 1)
label.font = UIFont(name: "Lato-Bold", size: 17.0)
label.backgroundColor = .clear
Expand All @@ -37,6 +39,7 @@ class UserGroupMemberCell: UITableViewCell {

let roleLabel: UILabel = {
let label = UILabel()
label.isOpaque = true
label.textColor = #colorLiteral(red: 0.7019607843, green: 0.7058823529, blue: 0.7137254902, alpha: 1)
label.backgroundColor = .clear
label.font = UIFont(name: "Lato-Regular", size: 13.0)
Expand All @@ -45,6 +48,7 @@ class UserGroupMemberCell: UITableViewCell {

lazy var memberDetailsStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [nameLabel, roleLabel])
stackView.isOpaque = true
stackView.axis = .vertical
stackView.alignment = .leading
stackView.translatesAutoresizingMaskIntoConstraints = false
Expand All @@ -53,6 +57,7 @@ class UserGroupMemberCell: UITableViewCell {

lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [avatarView, memberDetailsStackView])
stackView.isOpaque = true
stackView.alignment = .center
stackView.spacing = 16.0
stackView.translatesAutoresizingMaskIntoConstraints = false
Expand All @@ -68,6 +73,7 @@ class UserGroupMemberCell: UITableViewCell {
isAccessibilityElement = true

let backgroundView = UIView()
backgroundView.isOpaque = true
backgroundView.backgroundColor = #colorLiteral(red: 0.8196078431, green: 0.8235294118, blue: 0.8274509804, alpha: 1).withAlphaComponent(0.11)
selectedBackgroundView = backgroundView

Expand Down