I've got a program with slide out menu. There's Menu button(BarButtonItem) in the right position. When the view controller loads I'm doing next
_menuBarButton.target = self.revealViewController;
_menuBarButton.action = #selector(revealToggle:);
So, when I click on the this button the View Controller slides out to right and I see another View Controller.
So, I want to make the main view controller blur when it slides out. I've got a code how to do it blur, but I can't implement this code because when I tap on the bat button it runs revealToggle: selector.
I've tried:
1. To set action for bar button. And firstly blur view controller:
- (IBAction)menuBarButtonTapped:(UIBarButtonItem *)sender {
[self setBlurEffect];
_menuBarButton.target = self.revealViewController;
_menuBarButton.action = #selector(revealToggle:);
[self.menuBarButton.target performSelector:#selector(revealToggle:)];
}
But app crashes with "unrecognized selector" (the solutions of this problem doesn
t help too).
I've wanted to use willDisapear method but it doesn't run, because the main view controller doesn't disapear. It just slides out.
So, could you help me with this problem?
P.S. I'll be happy if you propose any other effects for main view controller except blur.
P.P.S. Sorry for many mistakes in question.
I've found a good answer for me. I don't use several ViewControllers to implement menu panel.
Now I'm using another UIView calls menuPanel. It contains tableView(UITableView) and blurView(UIView). The second one is under first one.
You can blur this view in to ways:
From the code using next method.
(void) setBlurEffect {// Add blur view
CGRect boundsView = self.someView.bounds;
UIVisualEffectView *tableViewVisualEffect = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]];
self.tableViewVisualEffect.frame = boundsView;
self.tableViewVisualEffect.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.tableViewVisualEffect.backgroundColor = [UIColor clearColor];
[UIView transitionWithView:self.view
duration:0.3
options:UIViewAnimationOptionTransitionCrossDissolve
animations: ^ {
[self.someView addSubview:self.tableViewVisualEffect];
}
completion: nil ];
// Here you can add visual effects to any UIView control.
// Replace custom view with navigation bar in above code to add effects to custom view.
}
Where someView is view you want to do blur(or any other effect)
From the story board with Visual Effect View.(I've chosen)
After all I've set tableView and blurView into menuPanelView as pinned to all sides of the menuPanel with 0 distance.
And the last thing I've done I'm changing the position of the menuPanelView with animation:
[self.menuPanelView setFrame:self.visibleTableViewFramePosition];
[UIView animateWithDuration:0.8
animations:^{
[self.menuPanelView setFrame:self.invisibleTableViewFramePosition];
} completion:nil];
Where invisibleTableViewFramePosition is CGRect variable contains position of the menuPanelView when it has to be invisible. You also need to create visibleTableViewFramePosition.
That's all. Hope it may be helpful for someone=)
Related
I have 1custom UIView in my default view in viewcontroller.My need is that if a button is clicked my default main view goes to blur and custom view remains unchanged.And a button is clicked again the blured view returns to normal effect. I used the UIBlurEffect and UIVisualEffectView for making blur. But the problem is whole view got blured.
I've not tried the effects views yet, but they look like they participate as good citizens in the view hierarchy. If that's true, then this should work...
// assume aView is the view you want blurred, and
// aSubview is the view you want to remain unblurred
[aSubview removeFromSuperview];
// apply the effect view to aView
[aView addSubview:aSubview];
Apply it to your view only you want to make it blur. Try it:
UIVisualEffect *MyblurEffect;
MyblurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
UIVisualEffectView *visualEffectView;
visualEffectView = [[UIVisualEffectView alloc] initWithEffect:MyblurEffect];
visualEffectView.frame = MyVIEW.bounds;
[MyVIEW addSubview:visualEffectView]; //MyVIEW-> your view
Try with these link:
How to apply blur to a UIView?
Maybe this is my strange understanding of the NSView hierarchy, but the question is:
I have a window with a SplitViewController which has the classic two child views.
When the user clic a button on the toolbar I add a 'work in progress' view as follows:
NSView* workingView;
// creating the view
...
[self.view addSubView:workingView];
where self is the SplitViewController object. My understanding (also from iOS programming) is that the split view controller has three views: left and right views and the workingView onto them(?)
The problem is that, even if workingView is covering the entire window, the two views are still accessible. In iOS developing we can set the userInteraction to NO so only the desired view is active. What about OSX?
Can I disable interaction with the split view controller views and let only the workingView be active?
Thanks
EDIT:
I followed #Wain hint and I created a new ViewController that contains my work in progress view.
I presented it using the presentViewController:animator technique. In this way I can hav more control about the positioning of the view.
In the custom animator I simply colored the view to see what is happening. Really the way the animator adds the new view controller seems nearly the same as adding a classic view onto the split view (like did before). In fact the result is the same: even if my working view covers two buttons belonging to the left side of the split view, they are clickable!
-(void)animatePresentationOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
NSViewController* bottomVC = fromViewController;
NSViewController* topVC = viewController;
topVC.view.wantsLayer = YES;
topVC.view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
topVC.view.alphaValue = 0.0f;
[bottomVC.view addSubview:topVC.view];
topVC.view.layer.backgroundColor = [[NSColor blueColor] CGColor];
topVC.view.frame = CGRectMake(10, 10, 100, 100);
[NSAnimationContext runAnimationGroup:^(NSAnimationContext* context){
context.duration = 1.0f;
topVC.view.animator.alphaValue = 1.0f;
} completionHandler:nil];
}
I know something is missing or wrong... May you help me in understanding what is going on here?
You should create a new view controller to manage your new view and then present it as a form sheet. The display style is slightly different on each platform but this is the correct approach.
An alternative is the screenshot the split view and pass the image to the new controller which is presented full size. It then applies an overlay and adds its own content view in front of the background image.
I have a view controller that on certain action presents another view controller that covers bottom half of the screen.
When I animate the second view controller (presented view controller) from the bottom to cover the bottom half of the screen, I also want animate the presenting view to top half of the screen. See the code below -
- (void)animatePresentationWithTransitioningContext:(id<UIViewControllerContextTransitioning>) transitioningContext
{
UIViewController *presentedController = [transitioningContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *presentedControllerView = [transitioningContext viewForKey:UITransitionContextToViewKey];
UIView *containerView = [transitioningContext containerView];
presentedControllerView.frame = [transitioningContext finalFrameForViewController:presentedController];
CGPoint center = presentedControllerView.center;
center.y = containerView.bounds.size.height;
presentedControllerView.center = center;
[containerView addSubview:presentedControllerView];
// This returns nil
UIView *presentingControllerView = [transitioningContext viewForKey:UITransitionContextFromViewKey];
[UIView animateWithDuration:[self transitionDuration:transitioningContext] delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
CGPoint presentedViewCenter = presentedControllerView.center;
presentedViewCenter.y -= containerView.bounds.size.height/2.0;
presentedControllerView.center = presentedViewCenter;
// This is where I want to move the presenting view controller.
// of course it does not work.
CGPoint presentingViewCenter = presentingControllerView.center;
presentingViewCenter.y -= containerView.bounds.size.height/2.0;
presentingControllerView.center = presentingViewCenter;
} completion:^(BOOL finished) {
[transitioningContext completeTransition:finished];
}];
}
The viewForKey:UITransitionContextFromViewKey returns nil. I overrode shouldRemovePresentersView to return NO in my custom UIPresentationController but I still get nil. I am using UIModalPresentationCustom as the modal presentation style for the view that I am presenting.
EDIT:
I realized that I can achieve the same affect by animating the presenting view by overriding the presentationTransitionWillBegin method in my custom UIPresentationController. But I would like to get an answer on accessing the presenting view controller.
EDIT:
Since I have found a solution, clarifying my question: (1) Is this the expected behavior i.e. the presentingViewController is going to be nil in the animator object?
I am using UIModalPresentationCustom as the modal presentation style for the view that I am presenting.
Well, there's your problem. In that case, the From view will be nil. It is nil unless you are using the FullScreen presentation style.
I'm not saying you are wrong to make the presentation style Custom. You need to do that, if you want to leave the presenting view controller's view in place, and if you want to supply your own presentation controller. And you do! But in that case, the work is divided: it is the presentation controller that becomes responsible for the final position of the view.
By the way, it occurs to me (thinking about it some more) that you are doing something you should probably not be doing. You are not expected to move the presenting view controller's view. It might be safer to take a snapshot view of the presenting view controller's view and animate that, behind your presented view controller's view.
I'm using the iCarousel library to show a coverflow-like UI. Animating the iCarousel subviews directly tends to blow up, so I create a wrapper for my animation and stick a subview inside of it which looks like the tapped cover. Then I hide the actual iCarousel view, and animate the fake cover on top of it.
I'm using UIView's transitionWithView:duration:options:animations:completion: method, but I'm running into some trouble. When animating another view onscreen for the first time, the view appears without any animation. When animating out, the view correctly flips and hides.
My view hierarchy is as follows:
main view, loaded from a nib
wrapper view
view to transition from
view to transition to
The view I'm transitioning to is a UINavigationController's view which contains a UITableViewController subclass. Instead of the initial animation, the UINavigationController appears and then the view grows upward a little, as if it's taking over the space otherwise occupied by a status bar.
Any idea why the table view might be animating like this? (I suspect containment APIs and/or wantsFullscreen, although I'm not explicitly using them. I simply install the views into the wrapper via addSubview:.)
Here's my "flip in" code, that animates every time but the first:
- (void) flipInWithCompletion:(MBTransitionCompletion)completion {
BOOL displayingPrimary = [self isDisplayingPrimaryView];
UIView *frontView = [self frontView];
UIView *backView = [self backView];
UIView *wrapperView = [self wrapperView];
[wrapperView addSubview:frontView];
[UIView transitionWithView:wrapperView
duration:0.8
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[wrapperView addSubview:backView];
[frontView removeFromSuperview];
}
completion:^(BOOL finished) {
[self setIsDisplayingPrimaryView:!displayingPrimary];
if (completion) {
completion();
}
}
];
}
What might cause the table view to grow instead of allowing the wrapper to flip?
Edit:
I've made a video demoing the exact problem.
Sounds like the view isn't properly loaded before it starts to animate in. I think I remember having a similar problem before. Try adding it to your view first, then remove it, and try to animate it into place to see if that fixes it.
That's not really a solution though, but it should give you a clue as to what might be wrong.
In one of my iPhone projects, I have three views that you can move around by touching and dragging. However, I want to stop the user from moving two views at the same time, by using two fingers. I have therefore tried to experiment with UIView.exclusiveTouch, without any success.
To understand how the property works, I created a brand new project, with the following code in the view controller:
- (void)loadView {
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
UIButton* a = [UIButton buttonWithType:UIButtonTypeInfoDark];
[a addTarget:self action:#selector(hej:) forControlEvents:UIControlEventTouchUpInside];
a.center = CGPointMake(50, 50);
a.multipleTouchEnabled = YES;
UIButton* b = [UIButton buttonWithType:UIButtonTypeInfoDark];
[b addTarget:self action:#selector(hej:) forControlEvents:UIControlEventTouchUpInside];
b.center = CGPointMake(200, 50);
b.multipleTouchEnabled = YES;
a.exclusiveTouch = YES;
[self.view addSubview:a];
[self.view addSubview:b];
}
- (void)hej:(id)sender
{
NSLog(#"hej: %#", sender);
}
When running this, hej: gets called, with different senders, when pressing any of the buttons - even though one of them has exclusiveTouch set to YES. I've tried commenting the multipleTouchEnabled-lines, to no avail. Can somebody explain to me what I'm missing here?
Thanks,
Eli
From The iPhone OS Programming Guide:
Restricting event delivery to a single view:
By default, a view’s exclusiveTouch property is set to NO. If you set
the property to YES, you mark the view so that, if it is tracking
touches, it is the only view in the window that is tracking touches.
Other views in the window cannot receive those touches. However, a
view that is marked “exclusive touch” does not receive touches that
are associated with other views in the same window. If a finger
contacts an exclusive-touch view, then that touch is delivered only if
that view is the only view tracking a finger in that window. If a
finger touches a non-exclusive view, then that touch is delivered only
if there is not another finger tracking in an exclusive-touch view.
It states that the exclusive touch property does NOT affect touches outside the frame of the view.
To handle this in the past, I use the main view to track ALL TOUCHES on screen instead of letting each subview track touches. The best way is to do:
if(CGRectContainsPoint(thesubviewIcareAbout.frame, theLocationOfTheTouch)){
//the subview has been touched, do what you want
}
I was encountering an issue like this where taps on my UIButtons were getting passed through to a tap gesture recognizer that I had attached to self.view, even though I was setting isExclusiveTouch to true on my UIButtons. Upon reviewing the materials here so far, I decided to put some code in my tap gesture code that checks if the tap location is contained in any UIButton frame and if that frame is also visible on the screen at the same time. If both of those conditions are true, then the UIButton will already have handled the tap, and the event triggered in my gesture recognizer can then be ignored as a pass through of the event. My logic allows me to loop over all subviews, checking if they are of type UIButton, and then checking if the tap was in that view and the view is visible.
#objc func singleTapped(tap: UITapGestureRecognizer)
{
anyControlsBreakpoint()
let tapPoint = tap.location(in: self.view)
// Prevent taps inside of buttons from passing through to singleTapped logic
for subview in self.view.subviews
{
if subview is UIButton {
if pointIsInFrameAndThatFrameIsVisible(view: subview, point: tapPoint)
{
return // Completely ignores pass through events that were already handled by a UIButton
}
}
}
Below is the code that checks if point was inside a visible button. Note that I hide my buttons by setting their alpha to zero. If you are using the isHidden property, your logic might need to look for that.
func pointIsInFrameAndThatFrameIsVisible(view : UIView, point : CGPoint) -> Bool
{
return view.frame.contains(point) && view.alpha == 1
}