Open VittoriDavide opened 5 years ago
Im having the same problem. Anybody found any solutions ?
I did work out a temporarily solution, i would not recommend it for long term but its a hotfix untill a better solution is presented.
Register two of the notifications do it where ever it makes sense to you like so:
if #available(iOS 13.0, *) {
NotificationCenter.default.addObserver(
self,
selector: #selector(self.menuDidOpeniOS13Fix),
name: NSNotification.Name(rawValue: "SlideNavigationControllerDidOpen"),
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.menuDidCloseiOS13Fix),
name: NSNotification.Name(rawValue: "SlideNavigationControllerDidClose"),
object: nil)
}
}
Implement two new functions to handle the open and close of the sidemenu witch is called from the above notifications. Please make sure that you have set your menu to not open, meaning that its witdth should be 0 pixels after the animation so that it do not open at all, these two new functions will take care of that:
@objc private func menuDidOpeniOS13Fix(){
let subviews = self.view.window?.subviews
if let view = subviews?[1]{
if let frame = self.navigationController?.view.frame{
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: {
view.frame = CGRect(x: 400, y: 0, width: frame.size.width, height: frame.size.height)
}) { (finished) in
}
}
}
}
@objc private func menuDidCloseiOS13Fix(){
let subviews = self.view.window?.subviews
if let view = subviews?[1]{
if let frame = self.navigationController?.view.frame{
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: {
view.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
}) { (finished) in
}
}
}
}
Setting the size of the side menu, for ios13 i make sure that the sidemenu wont open, as the new hotfix will animate the transitionview insted. For iOS <12 do as you always did.
if #available(iOS 13.0, *) {
SlideNavigationController.sharedInstance()?.landscapeSlideOffset = pointHeight
SlideNavigationController.sharedInstance()?.portraitSlideOffset = pointWidth
} else {
SlideNavigationController.sharedInstance()?.landscapeSlideOffset = pointHeight-LeftMenuViewController.widthOfLeftMenu
SlideNavigationController.sharedInstance()?.portraitSlideOffset = pointWidth-LeftMenuViewController.widthOfLeftMenu
}
there is alot of small issues with this mainly associated with rotating the device, i dont have time to solved these small issues for now, but its a starting point and a temp fix untill the problem is solved in the pod, if ever......
Please post any improvements that you may add.
I solved this by changing the order of the views when opening and closing the menu, then I added some tap-through code to the main view of the menu.
When the menu is open, it will actually cover the entire screen, but it's only the menu part that has any subviews. The rest will be transparent. When tapping on the transparent part, it will send the tap through to the underlying layer that closes the menu.
In your storyboard, change the width of everything you can on your menu to be exactly 190 since that's the default width of the menu. Don't use stretching width or anything or it will not look good on different devices.
Also change the background-color of the main menu view to be "clear color".
Create a new class that inherits from UIView and override pointInside and use this for your main view in the menu (set it in the storyboard). It basically just says that the main view will let taps go through to the underlying layer while taps on the subviews (the menu) will be caught. I called my class "ClickThroughView":
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView *view in self.subviews) {
if (!view.hidden && view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event])
return YES;
}
return NO;
}
Code from https://stackoverflow.com/a/4010809/3727143
Now in SlideNavigationController.m, make the following change at around line 600:
[removingMenuViewController.view removeFromSuperview];
menuViewController.view.tag = 200; // The actual menu
self.view.window.subviews.lastObject.tag = 100; // The problematic view
[self.view.window insertSubview:menuViewController.view atIndex:0];
Also change the openMenu and closeMenu methods, adding view rearrange code. Just a single line in both methods.
(void)openMenu:
[self.view.window sendSubviewToBack:[self.view.window viewWithTag:100]];
(void)closeMenuWithDuration:
[self.view.window sendSubviewToBack:[self.view.window viewWithTag:200]];
- (void)openMenu:(Menu)menu withDuration:(float)duration andCompletion:(void (^)())completion
{
[self enableTapGestureToCloseMenu:YES];
[self prepareMenuForReveal:menu forcePrepare:NO];
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect rect = self.view.frame;
CGFloat width = self.horizontalSize;
rect.origin.x = (menu == MenuLeft) ? (self.slideOffset) : ((self.slideOffset )* -1);
[self moveHorizontallyToLocation:rect.origin.x];
}
completion:^(BOOL finished) {
[self.view.window sendSubviewToBack:[self.view.window viewWithTag:100]];
if (completion)
completion();
}];
}
- (void)closeMenuWithDuration:(float)duration andCompletion:(void (^)())completion
{
[self enableTapGestureToCloseMenu:NO];
[self.view.window sendSubviewToBack:[self.view.window viewWithTag:200]];
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect rect = self.view.frame;
rect.origin.x = 0;
[self moveHorizontallyToLocation:rect.origin.x];
}
completion:^(BOOL finished) {
if (completion)
completion();
}];
}
The shadow will not work properly after this, it will only be visible during transition so I just removed it:
#define MENU_SHADOW_OPACITY 0
This way the slide menu works on all devices, both iOS 12 and iOS 13.
I solved this by changing the order of the views when opening and closing the menu, then I added some tap-through code to the main view of the menu.
When the menu is open, it will actually cover the entire screen, but it's only the menu part that has any subviews. The rest will be transparent. When tapping on the transparent part, it will send the tap through to the underlying layer that closes the menu.
In your storyboard, change the width of everything you can on your menu to be exactly 190 since that's the default width of the menu. Don't use stretching width or anything or it will not look good on different devices.
Also change the background-color of the main menu view to be "clear color".
Create a new class that inherits from UIView and override pointInside and use this for your main view in the menu (set it in the storyboard). It basically just says that the main view will let taps go through to the underlying layer while taps on the subviews (the menu) will be caught. I called my class "ClickThroughView":
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { for (UIView *view in self.subviews) { if (!view.hidden && view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) return YES; } return NO; }
Code from https://stackoverflow.com/a/4010809/3727143
Now in SlideNavigationController.m, make the following change at around line 600:
[removingMenuViewController.view removeFromSuperview]; menuViewController.view.tag = 200; // The actual menu self.view.window.subviews.lastObject.tag = 100; // The problematic view [self.view.window insertSubview:menuViewController.view atIndex:0];
Also change the openMenu and closeMenu methods, adding view rearrange code. Just a single line in both methods.
(void)openMenu:
[self.view.window sendSubviewToBack:[self.view.window viewWithTag:100]];
(void)closeMenuWithDuration:
[self.view.window sendSubviewToBack:[self.view.window viewWithTag:200]];
- (void)openMenu:(Menu)menu withDuration:(float)duration andCompletion:(void (^)())completion { [self enableTapGestureToCloseMenu:YES]; [self prepareMenuForReveal:menu forcePrepare:NO]; [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ CGRect rect = self.view.frame; CGFloat width = self.horizontalSize; rect.origin.x = (menu == MenuLeft) ? (self.slideOffset) : ((self.slideOffset )* -1); [self moveHorizontallyToLocation:rect.origin.x]; } completion:^(BOOL finished) { [self.view.window sendSubviewToBack:[self.view.window viewWithTag:100]]; if (completion) completion(); }]; }
- (void)closeMenuWithDuration:(float)duration andCompletion:(void (^)())completion { [self enableTapGestureToCloseMenu:NO]; [self.view.window sendSubviewToBack:[self.view.window viewWithTag:200]]; [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ CGRect rect = self.view.frame; rect.origin.x = 0; [self moveHorizontallyToLocation:rect.origin.x]; } completion:^(BOOL finished) { if (completion) completion(); }]; }
The shadow will not work properly after this, it will only be visible during transition so I just removed it:
#define MENU_SHADOW_OPACITY 0
This way the slide menu works on all devices, both iOS 12 and iOS 13.
After following your code. My application no longer has the above error . How to swipe left to return to the main screen of the application
I have a different solution
I declared a problemView
@implementation SlideNavigationController{
.
.
.
UIView *problemView;
}
- (void)prepareMenuForReveal:(Menu)menu
{
// Only prepare menu if it has changed (ex: from MenuLeft to MenuRight or vice versa)
//if (self.lastRevealedMenu && menu == self.lastRevealedMenu)
// return;
UIViewController *menuViewController = (menu == MenuLeft) ? self.leftMenu : self.rightMenu;
UIViewController *removingMenuViewController = (menu == MenuLeft) ? self.rightMenu : self.leftMenu;
self.lastRevealedMenu = menu;
[removingMenuViewController.view removeFromSuperview];
//ipad ios13
for (UIView *subview in [[[UIApplication sharedApplication] delegate] window].subviews) {
if ([subview isKindOfClass:NSClassFromString(@"UITransitionView")]) {
for (UIView *subview2 in subview.subviews) {
if ([subview2 isKindOfClass:NSClassFromString(@"UIDropShadowView")]) {
for (UIView *subview3 in subview2.subviews) {
if ([subview3 isKindOfClass:NSClassFromString(@"UIView")]) {
problemView = subview3;
}
}
}
}
}
}
- (CGFloat)horizontalLocation
{
CGRect rect = self.view.frame;
if (problemView) {
rect = problemView.frame;
}
- (void)moveHorizontallyToLocation:(CGFloat)location
{
CGRect rect = self.view.frame;
Menu menu = (self.horizontalLocation >= 0 && location >= 0) ? MenuLeft : MenuRight;
if ((location > 0 && self.horizontalLocation <= 0) || (location < 0 && self.horizontalLocation >= 0)) {
[self postNotificationWithName:SlideNavigationControllerDidReveal forMenu:(location > 0) ? MenuLeft : MenuRight];
}
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0"))
{
rect.origin.x = location;
rect.origin.y = 0;
}
else
{
if (UIDeviceOrientationIsLandscape(self.lastValidDeviceInterfaceOrientation))
{
rect.origin.x = 0;
rect.origin.y = (self.lastValidDeviceInterfaceOrientation == UIDeviceOrientationLandscapeRight) ? location*-1 : location;
}
else
{
rect.origin.x = (self.lastValidDeviceInterfaceOrientation == UIDeviceOrientationPortrait) ? location : location*-1;
rect.origin.y = 0;
}
}
//[[self.view.window viewWithTag:100] setFrame:rect];
if (problemView) {
[problemView setFrame:rect];
self.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
else{
self.view.frame = rect;
}
[self updateMenuAnimation:menu];
}
I tested on iPad and iPhone both ios13 and ios12. It workings fine. And When menu opened right side not disappeared
Ohh sorry identifying problemView must be like this
for (UIView *subview in [[[UIApplication sharedApplication] delegate] window].subviews) {
if ([subview isKindOfClass:NSClassFromString(@"UITransitionView")]) {
for (UIView *subview2 in subview.subviews) {
if ([subview2 isKindOfClass:NSClassFromString(@"UIDropShadowView")]) {
for (UIView *subview3 in subview2.subviews) {
if ([subview3 isKindOfClass:NSClassFromString(@"UIView")] && ![subview3 isKindOfClass:NSClassFromString(@"UILayoutContainerView")]) {
problemView = subview3;
}
}
}
}
}
}
Ohh sorry identifying problemView must be like this
for (UIView *subview in [[[UIApplication sharedApplication] delegate] window].subviews) { if ([subview isKindOfClass:NSClassFromString(@"UITransitionView")]) { for (UIView *subview2 in subview.subviews) { if ([subview2 isKindOfClass:NSClassFromString(@"UIDropShadowView")]) { for (UIView *subview3 in subview2.subviews) { if ([subview3 isKindOfClass:NSClassFromString(@"UIView")] && ![subview3 isKindOfClass:NSClassFromString(@"UILayoutContainerView")]) { problemView = subview3; } } } } } }
Thanks @codaman !!! :)
@codaman self.lastValidDeviceInterfaceOrientation. How did you declare it
@kilirushi you could do something like:
@property (nonatomic, assign) UIDeviceOrientation lastValidDeviceInterfaceOrientation;
just bellow the menuNeedsLayout
property.
I have a different solution
I declared a problemView
@implementation SlideNavigationController{ . . . UIView *problemView; }
- (void)prepareMenuForReveal:(Menu)menu { // Only prepare menu if it has changed (ex: from MenuLeft to MenuRight or vice versa) //if (self.lastRevealedMenu && menu == self.lastRevealedMenu) // return; UIViewController *menuViewController = (menu == MenuLeft) ? self.leftMenu : self.rightMenu; UIViewController *removingMenuViewController = (menu == MenuLeft) ? self.rightMenu : self.leftMenu; self.lastRevealedMenu = menu; [removingMenuViewController.view removeFromSuperview]; //ipad ios13 for (UIView *subview in [[[UIApplication sharedApplication] delegate] window].subviews) { if ([subview isKindOfClass:NSClassFromString(@"UITransitionView")]) { for (UIView *subview2 in subview.subviews) { if ([subview2 isKindOfClass:NSClassFromString(@"UIDropShadowView")]) { for (UIView *subview3 in subview2.subviews) { if ([subview3 isKindOfClass:NSClassFromString(@"UIView")]) { problemView = subview3; } } } } } }
- (CGFloat)horizontalLocation { CGRect rect = self.view.frame; if (problemView) { rect = problemView.frame; }
- (void)moveHorizontallyToLocation:(CGFloat)location { CGRect rect = self.view.frame; Menu menu = (self.horizontalLocation >= 0 && location >= 0) ? MenuLeft : MenuRight; if ((location > 0 && self.horizontalLocation <= 0) || (location < 0 && self.horizontalLocation >= 0)) { [self postNotificationWithName:SlideNavigationControllerDidReveal forMenu:(location > 0) ? MenuLeft : MenuRight]; } if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) { rect.origin.x = location; rect.origin.y = 0; } else { if (UIDeviceOrientationIsLandscape(self.lastValidDeviceInterfaceOrientation)) { rect.origin.x = 0; rect.origin.y = (self.lastValidDeviceInterfaceOrientation == UIDeviceOrientationLandscapeRight) ? location*-1 : location; } else { rect.origin.x = (self.lastValidDeviceInterfaceOrientation == UIDeviceOrientationPortrait) ? location : location*-1; rect.origin.y = 0; } } //[[self.view.window viewWithTag:100] setFrame:rect]; if (problemView) { [problemView setFrame:rect]; self.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); } else{ self.view.frame = rect; } [self updateMenuAnimation:menu]; }
I tested on iPad and iPhone both ios13 and ios12. It workings fine. And When menu opened right side not disappeared
This solution worked perfectly for me. Tested in IOS 15, Ipad
You can also change one line of code in SlideNavigationController.m
[self.view.window insertSubview:menuViewController.view atIndex:0];
change window
to superview
You can also change one line of code in SlideNavigationController.m
[self.view.window insertSubview:menuViewController.view atIndex:0];
change
window
tosuperview
Mind. Blown. 🤯
Thanks!
When you open the left slide menu you can see all the View Controller but you cannot click on the button, like if there were an invisible layer preventing to click. Does anybody know why this could be happening on the new OS of the iPad? On iOS 12 and on iOS 13 in the iPhone, it is working perfectly
Seems like it is adding an UITransitionView, upper of everything, the problems continues even if setUserInteractionEnabled is set to no in the transition view