Skip to content

Commit

Permalink
Fix hang on iOS 14 Beta 4
Browse files Browse the repository at this point in the history
  • Loading branch information
florianbuerger committed Aug 5, 2020
1 parent a0cded5 commit ea6c287
Showing 1 changed file with 171 additions and 161 deletions.
332 changes: 171 additions & 161 deletions PulleyLib/PulleyViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -790,174 +790,184 @@ open class PulleyViewController: UIViewController, PulleyDrawerViewControllerDel

override open func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

// Make sure our view controller views are subviews of the right view (Resolves #21 issue with changing the presentation context)

// May be nil during initial layout
if let primary = primaryContentViewController
{
if primary.view.superview != nil && primary.view.superview != primaryContentContainer
{
primaryContentContainer.addSubview(primary.view)
primaryContentContainer.sendSubviewToBack(primary.view)

primary.view.constrainToParent()
}
}

// May be nil during initial layout
if let drawer = drawerContentViewController
{
if drawer.view.superview != nil && drawer.view.superview != drawerContentContainer
{
drawerContentContainer.addSubview(drawer.view)
drawerContentContainer.sendSubviewToBack(drawer.view)

drawer.view.constrainToParent()
}
}

let safeAreaTopInset = pulleySafeAreaInsets.top
let safeAreaBottomInset = pulleySafeAreaInsets.bottom
let safeAreaLeftInset = pulleySafeAreaInsets.left
let safeAreaRightInset = pulleySafeAreaInsets.right
// Since iOS 14 Beta 4 the system just hangs when doing layout right after the views layout has finished
// I haven't investigated further if that is an actual bug in iOS or in Pulley. This workaround solved it
// for us. Tested on iOS 13 & 14 Beta 4
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.setupLayout()
}
}


let displayModeForCurrentLayout: PulleyDisplayMode = displayMode != .automatic ? displayMode : ((self.view.bounds.width >= 600.0 || self.traitCollection.horizontalSizeClass == .regular) ? .panel : .drawer)

currentDisplayMode = displayModeForCurrentLayout

if displayModeForCurrentLayout == .drawer
{
// Bottom inset for safe area / bottomLayoutGuide
if #available(iOS 11, *) {
self.drawerScrollView.contentInsetAdjustmentBehavior = .scrollableAxes
} else {
self.automaticallyAdjustsScrollViewInsets = false
self.drawerScrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: self.bottomLayoutGuide.length, right: 0)
self.drawerScrollView.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: self.bottomLayoutGuide.length, right: 0) // (usefull if visible..)
}
// MARK: Private State Updates

let lowestStop = getStopList().min() ?? 0

let adjustedLeftSafeArea = adjustDrawerHorizontalInsetToSafeArea ? safeAreaLeftInset : 0.0
let adjustedRightSafeArea = adjustDrawerHorizontalInsetToSafeArea ? safeAreaRightInset : 0.0

if supportedPositions.contains(.open)
{
// Layout scrollview
drawerScrollView.frame = CGRect(x: adjustedLeftSafeArea, y: drawerTopInset + safeAreaTopInset, width: self.view.bounds.width - adjustedLeftSafeArea - adjustedRightSafeArea, height: heightOfOpenDrawer)
}
else
{
// Layout scrollview
let adjustedTopInset: CGFloat = getStopList().max() ?? 0.0
drawerScrollView.frame = CGRect(x: adjustedLeftSafeArea, y: self.view.bounds.height - adjustedTopInset, width: self.view.bounds.width - adjustedLeftSafeArea - adjustedRightSafeArea, height: adjustedTopInset)
}

drawerScrollView.addSubview(drawerShadowView)

if let drawerBackgroundVisualEffectView = drawerBackgroundVisualEffectView
{
drawerScrollView.addSubview(drawerBackgroundVisualEffectView)
drawerBackgroundVisualEffectView.layer.cornerRadius = drawerCornerRadius
}

drawerScrollView.addSubview(drawerContentContainer)

drawerContentContainer.frame = CGRect(x: 0, y: drawerScrollView.bounds.height - lowestStop, width: drawerScrollView.bounds.width, height: drawerScrollView.bounds.height + bounceOverflowMargin)
drawerBackgroundVisualEffectView?.frame = drawerContentContainer.frame
drawerShadowView.frame = drawerContentContainer.frame
drawerScrollView.contentSize = CGSize(width: drawerScrollView.bounds.width, height: (drawerScrollView.bounds.height - lowestStop) + drawerScrollView.bounds.height - safeAreaBottomInset + (bounceOverflowMargin - 5.0))

// Update rounding mask and shadows
let borderPath = drawerMaskingPath(byRoundingCorners: [.topLeft, .topRight, .bottomLeft, .bottomRight]).cgPath

let cardMaskLayer = CAShapeLayer()
cardMaskLayer.path = borderPath
cardMaskLayer.frame = drawerContentContainer.bounds
cardMaskLayer.fillColor = UIColor.white.cgColor
cardMaskLayer.backgroundColor = UIColor.clear.cgColor
drawerContentContainer.layer.mask = cardMaskLayer
drawerShadowView.layer.shadowPath = borderPath

backgroundDimmingView.frame = CGRect(x: 0.0, y: 0.0, width: self.view.bounds.width, height: self.view.bounds.height + drawerScrollView.contentSize.height)

drawerScrollView.transform = CGAffineTransform.identity

backgroundDimmingView.isHidden = false
}
else
{
// Bottom inset for safe area / bottomLayoutGuide
if #available(iOS 11, *) {
self.drawerScrollView.contentInsetAdjustmentBehavior = .scrollableAxes
} else {
self.automaticallyAdjustsScrollViewInsets = false
self.drawerScrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0.0, right: 0)
self.drawerScrollView.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0.0, right: 0)
}

// Layout container
var collapsedHeight:CGFloat = kPulleyDefaultCollapsedHeight
var partialRevealHeight:CGFloat = kPulleyDefaultPartialRevealHeight

if let drawerVCCompliant = drawerContentViewController as? PulleyDrawerViewControllerDelegate
{
collapsedHeight = drawerVCCompliant.collapsedDrawerHeight?(bottomSafeArea: safeAreaBottomInset) ?? kPulleyDefaultCollapsedHeight
partialRevealHeight = drawerVCCompliant.partialRevealDrawerHeight?(bottomSafeArea: safeAreaBottomInset) ?? kPulleyDefaultPartialRevealHeight
}

let lowestStop = [(self.view.bounds.size.height - panelInsets.bottom - safeAreaTopInset), collapsedHeight, partialRevealHeight].min() ?? 0

let xOrigin = (panelCornerPlacement == .bottomLeft || panelCornerPlacement == .topLeft) ? (safeAreaLeftInset + panelInsets.left) : (self.view.bounds.maxX - (safeAreaRightInset + panelInsets.right) - panelWidth)

let yOrigin = (panelCornerPlacement == .bottomLeft || panelCornerPlacement == .bottomRight) ? (panelInsets.top + safeAreaTopInset) : (panelInsets.top + safeAreaTopInset + bounceOverflowMargin)

if supportedPositions.contains(.open)
{
// Layout scrollview
drawerScrollView.frame = CGRect(x: xOrigin, y: yOrigin, width: panelWidth, height: heightOfOpenDrawer)
}
else
{
// Layout scrollview
let adjustedTopInset: CGFloat = supportedPositions.contains(.partiallyRevealed) ? partialRevealHeight : collapsedHeight
drawerScrollView.frame = CGRect(x: xOrigin, y: yOrigin, width: panelWidth, height: adjustedTopInset)
}
private func setupLayout() {
// Make sure our view controller views are subviews of the right view (Resolves #21 issue with changing the presentation context)

syncDrawerContentViewSizeToMatchScrollPositionForSideDisplayMode()

drawerScrollView.contentSize = CGSize(width: drawerScrollView.bounds.width, height: self.view.bounds.height + (self.view.bounds.height - lowestStop))

switch panelCornerPlacement {
case .topLeft, .topRight:
drawerScrollView.transform = CGAffineTransform(scaleX: 1.0, y: -1.0)
case .bottomLeft, .bottomRight:
drawerScrollView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
}

backgroundDimmingView.isHidden = true
}

drawerContentContainer.transform = drawerScrollView.transform
drawerShadowView.transform = drawerScrollView.transform
drawerBackgroundVisualEffectView?.transform = drawerScrollView.transform

let lowestStop = getStopList().min() ?? 0

delegate?.drawerChangedDistanceFromBottom?(drawer: self, distance: drawerScrollView.contentOffset.y + lowestStop, bottomSafeArea: pulleySafeAreaInsets.bottom)
(drawerContentViewController as? PulleyDrawerViewControllerDelegate)?.drawerChangedDistanceFromBottom?(drawer: self, distance: drawerScrollView.contentOffset.y + lowestStop, bottomSafeArea: pulleySafeAreaInsets.bottom)
(primaryContentViewController as? PulleyPrimaryContentControllerDelegate)?.drawerChangedDistanceFromBottom?(drawer: self, distance: drawerScrollView.contentOffset.y + lowestStop, bottomSafeArea: pulleySafeAreaInsets.bottom)

maskDrawerVisualEffectView()
maskBackgroundDimmingView()
setDrawerPosition(position: drawerPosition, animated: false)
// May be nil during initial layout
if let primary = primaryContentViewController
{
if primary.view.superview != nil && primary.view.superview != primaryContentContainer
{
primaryContentContainer.addSubview(primary.view)
primaryContentContainer.sendSubviewToBack(primary.view)

primary.view.constrainToParent()
}
}

// May be nil during initial layout
if let drawer = drawerContentViewController
{
if drawer.view.superview != nil && drawer.view.superview != drawerContentContainer
{
drawerContentContainer.addSubview(drawer.view)
drawerContentContainer.sendSubviewToBack(drawer.view)

drawer.view.constrainToParent()
}
}

let safeAreaTopInset = pulleySafeAreaInsets.top
let safeAreaBottomInset = pulleySafeAreaInsets.bottom
let safeAreaLeftInset = pulleySafeAreaInsets.left
let safeAreaRightInset = pulleySafeAreaInsets.right


let displayModeForCurrentLayout: PulleyDisplayMode = displayMode != .automatic ? displayMode : ((self.view.bounds.width >= 600.0 || self.traitCollection.horizontalSizeClass == .regular) ? .panel : .drawer)

currentDisplayMode = displayModeForCurrentLayout

if displayModeForCurrentLayout == .drawer
{
// Bottom inset for safe area / bottomLayoutGuide
if #available(iOS 11, *) {
self.drawerScrollView.contentInsetAdjustmentBehavior = .scrollableAxes
} else {
self.automaticallyAdjustsScrollViewInsets = false
self.drawerScrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: self.bottomLayoutGuide.length, right: 0)
self.drawerScrollView.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: self.bottomLayoutGuide.length, right: 0) // (usefull if visible..)
}

let lowestStop = getStopList().min() ?? 0

let adjustedLeftSafeArea = adjustDrawerHorizontalInsetToSafeArea ? safeAreaLeftInset : 0.0
let adjustedRightSafeArea = adjustDrawerHorizontalInsetToSafeArea ? safeAreaRightInset : 0.0

if supportedPositions.contains(.open)
{
// Layout scrollview
drawerScrollView.frame = CGRect(x: adjustedLeftSafeArea, y: drawerTopInset + safeAreaTopInset, width: self.view.bounds.width - adjustedLeftSafeArea - adjustedRightSafeArea, height: heightOfOpenDrawer)
}
else
{
// Layout scrollview
let adjustedTopInset: CGFloat = getStopList().max() ?? 0.0
drawerScrollView.frame = CGRect(x: adjustedLeftSafeArea, y: self.view.bounds.height - adjustedTopInset, width: self.view.bounds.width - adjustedLeftSafeArea - adjustedRightSafeArea, height: adjustedTopInset)
}

drawerScrollView.addSubview(drawerShadowView)

if let drawerBackgroundVisualEffectView = drawerBackgroundVisualEffectView
{
drawerScrollView.addSubview(drawerBackgroundVisualEffectView)
drawerBackgroundVisualEffectView.layer.cornerRadius = drawerCornerRadius
}

drawerScrollView.addSubview(drawerContentContainer)

drawerContentContainer.frame = CGRect(x: 0, y: drawerScrollView.bounds.height - lowestStop, width: drawerScrollView.bounds.width, height: drawerScrollView.bounds.height + bounceOverflowMargin)
drawerBackgroundVisualEffectView?.frame = drawerContentContainer.frame
drawerShadowView.frame = drawerContentContainer.frame
drawerScrollView.contentSize = CGSize(width: drawerScrollView.bounds.width, height: (drawerScrollView.bounds.height - lowestStop) + drawerScrollView.bounds.height - safeAreaBottomInset + (bounceOverflowMargin - 5.0))

// Update rounding mask and shadows
let borderPath = drawerMaskingPath(byRoundingCorners: [.topLeft, .topRight, .bottomLeft, .bottomRight]).cgPath

let cardMaskLayer = CAShapeLayer()
cardMaskLayer.path = borderPath
cardMaskLayer.frame = drawerContentContainer.bounds
cardMaskLayer.fillColor = UIColor.white.cgColor
cardMaskLayer.backgroundColor = UIColor.clear.cgColor
drawerContentContainer.layer.mask = cardMaskLayer
drawerShadowView.layer.shadowPath = borderPath

backgroundDimmingView.frame = CGRect(x: 0.0, y: 0.0, width: self.view.bounds.width, height: self.view.bounds.height + drawerScrollView.contentSize.height)

drawerScrollView.transform = CGAffineTransform.identity

backgroundDimmingView.isHidden = false
}
else
{
// Bottom inset for safe area / bottomLayoutGuide
if #available(iOS 11, *) {
self.drawerScrollView.contentInsetAdjustmentBehavior = .scrollableAxes
} else {
self.automaticallyAdjustsScrollViewInsets = false
self.drawerScrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0.0, right: 0)
self.drawerScrollView.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0.0, right: 0)
}

// Layout container
var collapsedHeight:CGFloat = kPulleyDefaultCollapsedHeight
var partialRevealHeight:CGFloat = kPulleyDefaultPartialRevealHeight

if let drawerVCCompliant = drawerContentViewController as? PulleyDrawerViewControllerDelegate
{
collapsedHeight = drawerVCCompliant.collapsedDrawerHeight?(bottomSafeArea: safeAreaBottomInset) ?? kPulleyDefaultCollapsedHeight
partialRevealHeight = drawerVCCompliant.partialRevealDrawerHeight?(bottomSafeArea: safeAreaBottomInset) ?? kPulleyDefaultPartialRevealHeight
}

let lowestStop = [(self.view.bounds.size.height - panelInsets.bottom - safeAreaTopInset), collapsedHeight, partialRevealHeight].min() ?? 0

let xOrigin = (panelCornerPlacement == .bottomLeft || panelCornerPlacement == .topLeft) ? (safeAreaLeftInset + panelInsets.left) : (self.view.bounds.maxX - (safeAreaRightInset + panelInsets.right) - panelWidth)

let yOrigin = (panelCornerPlacement == .bottomLeft || panelCornerPlacement == .bottomRight) ? (panelInsets.top + safeAreaTopInset) : (panelInsets.top + safeAreaTopInset + bounceOverflowMargin)

if supportedPositions.contains(.open)
{
// Layout scrollview
drawerScrollView.frame = CGRect(x: xOrigin, y: yOrigin, width: panelWidth, height: heightOfOpenDrawer)
}
else
{
// Layout scrollview
let adjustedTopInset: CGFloat = supportedPositions.contains(.partiallyRevealed) ? partialRevealHeight : collapsedHeight
drawerScrollView.frame = CGRect(x: xOrigin, y: yOrigin, width: panelWidth, height: adjustedTopInset)
}

syncDrawerContentViewSizeToMatchScrollPositionForSideDisplayMode()

drawerScrollView.contentSize = CGSize(width: drawerScrollView.bounds.width, height: self.view.bounds.height + (self.view.bounds.height - lowestStop))

switch panelCornerPlacement {
case .topLeft, .topRight:
drawerScrollView.transform = CGAffineTransform(scaleX: 1.0, y: -1.0)
case .bottomLeft, .bottomRight:
drawerScrollView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
}

backgroundDimmingView.isHidden = true
}

drawerContentContainer.transform = drawerScrollView.transform
drawerShadowView.transform = drawerScrollView.transform
drawerBackgroundVisualEffectView?.transform = drawerScrollView.transform

let lowestStop = getStopList().min() ?? 0

delegate?.drawerChangedDistanceFromBottom?(drawer: self, distance: drawerScrollView.contentOffset.y + lowestStop, bottomSafeArea: pulleySafeAreaInsets.bottom)
(drawerContentViewController as? PulleyDrawerViewControllerDelegate)?.drawerChangedDistanceFromBottom?(drawer: self, distance: drawerScrollView.contentOffset.y + lowestStop, bottomSafeArea: pulleySafeAreaInsets.bottom)
(primaryContentViewController as? PulleyPrimaryContentControllerDelegate)?.drawerChangedDistanceFromBottom?(drawer: self, distance: drawerScrollView.contentOffset.y + lowestStop, bottomSafeArea: pulleySafeAreaInsets.bottom)

maskDrawerVisualEffectView()
maskBackgroundDimmingView()
setDrawerPosition(position: drawerPosition, animated: false)

initialLayoutDone = true
}

// MARK: Private State Updates
}

private func enforceCanScrollDrawer() {
guard isViewLoaded else {
Expand Down

0 comments on commit ea6c287

Please sign in to comment.