NSView redraw not working with setNeedsDisplay:YES; - objective-c

I have a custom view on which I want to force a redraw upon execution of an IBAction method implemented in the NSView controller subclass:
- (IBAction)forceRedraw:(id)sender {
[self setNeedsDisplay:YES];
[self display];
}
This seems to have no direct effect whatsoever. My custom view only gets redrawn once I mouse into the custom view area or resize the main window.
Any thoughts on why this isn't working?

Related

Capture my uibuttons event

I have a big problem in my iphone/ipad ios7 app, I have a lot of controls based on UIView.
for example
list in my view controller I'm adding list based on UIView, this list contains some controls subviews based on UIView, and this controls have a lot subviews (particulary uibuttons) too. And now I want get UIControlEventTouchUpInside action im my viewcontroller, how I can do that ? I Do delegate im my uibutton control but im my view controller I dont't have instance this button, so I can't use
myButton.delegate = self;
I Have just instance my SuperView.
Someone could help me?
It will be better if you use custom view class for your UIView which is you used for containing your button and other controls. In that custom class you can simply set the action for your controls.
EDIT
You can use the following method in your UIButton's custom class:
- (void)awakeFromNib {
[super awakeFromNib];
[self addTarget:self
action:#selector(yourClickMethod:)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)yourClickMethod:(UIButton *)infoButton {
// Button action goes here
}

AutoLayout won't resize view when rotating in a custom container view?

I have a very basic container view that contains a sidebar and swaps out the view controllers in the content area (think UISplitView but with a small icon sidebar / vertical UITabBar).
The container view controller uses autoLayout and resizes correctly when rotated.
Content viewController 1 uses autolayout and was made with IB, so it has a xib file.
Content viewController 2 inherits from UITableViewController and does not use a xib.
If I assign viewController 1 as the root view controller and rotate, the resize works and here are the callbacks that I get in viewController 1:
willRotateToInterfaceOrientation
updateViewConstraints
viewWillLayoutSubviews
didRotateFromInterfaceOrientation
However, if I assign my container view controller as the root view controller, load viewController 1 and rotate, the resize does not work. And I only get the following callbacks inside viewController 1:
willRotateToInterfaceOrientation
didRotateFromInterfaceOrientation
Inside my view controller container, here's how I swap the view controllers:
[self addChildViewController:toViewController];
[toViewController didMoveToParentViewController:self];
// Remove the old view controller
[fromViewController willMoveToParentViewController:nil];
[fromViewController.view removeFromSuperview];
[fromViewController removeFromParentViewController];
// Add the new view
[self.contentContainerView addSubview:toViewController.view];
Now, I do get the callbacks that a rotation is about to happen, but it seems as if neither updateViewConstraints nor viewWillLayoutSubviews is called. This explains why the resize is not happening, but why are those methods not called once I put the view controller in a container view?
I also tried to explicitly return YES in my container on both
shouldAutomaticallyForwardAppearanceMethods
and
shouldAutomaticallyForwardAppearanceMethods
although this should be the default already.
Also, the view controller not made with IB (view controller 2) resizes correctly when rotating inside the container. However, I don't explicitly use NSLayoutConstraints on this one, so I suspect it's defaulting to Springs and Struts for the resizing when rotating.
Do I need to forward some other events on my view controller container to get the auto layout view controller to resize correctly when rotating?
OK, I think I was missing this method here in my view controller container:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
self.contentViewController.view.frame = self.contentContainerView.bounds;
}
While this resizes now correctly when rotating, it still doesn't trigger
updateViewConstraints
in my child view controller. Interesting
Seems like iOS 8 does call updateViewConstraints for you. But iOS 7 didn't. To get this called in iOS 7, call setNeedsUpdateConstraints, like this:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:interfaceOrientation duration:duration];
BOOL isiOS7 = floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1;
if (isiOS7) {
// Trigger a call to updateViewConstraints
[self.view setNeedsUpdateConstraints];
}
}
In updateLayoutConstraints, a good way to check which orientation is the one to layout for is to check the status bar's orientation. This works for 7 and 8.
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
BOOL layoutAsLandscape = UIInterfaceOrientationIsLandscape(orientation);

UIPageViewController Traps All UITapGestureRecognizer Events

It's been a long day at the keyboard so I'm reaching out :-)
I have a UIPageViewController in a typical implementation that basically follows Apple's standard template. I am trying to add an overlay that will allow the user to do things like touch a button to jump to certain pages or dismiss the view controller to go to another part of the app.
My problem is that the UIPageViewController is trapping all events from my overlay subview and I am struggling to find a workable solution.
Here's some code to help the example...
In viewDidLoad
// Page creation, pageViewController creation etc....
self.pageViewController.delegate = self;
[self.pageViewController setViewControllers:pagesArray
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
self.pageViewController.dataSource = self;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// self.overlay being the overlay view
if (!self.overlay)
{
self.overlay = [[MyOverlayClass alloc] init]; // Gets frame etc from class init
[self.view addSubview:self.overlay];
}
This all works great. The overlay gets created, it gets show over the top of the pages of the UIPageViewController as you would expect. When pages flip, they flip underneath the overlay - again just as you would expect.
However, the UIButtons within the self.overlay view never get the tap events. The UIPageViewController responds to all events.
I have tried overriding -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch per the suggestions here without success.
UIPageViewController Gesture recognizers
I have tried manually trapping all events and handling them myself - doesn't work (and to be honest even if it did it would seem like a bit of a hack).
Does anyone have a suggestion on how to trap the events or maybe a better approach to using an overlay over the top of the UIPageViewController.
Any and all help very much appreciated!!
Try to iterate through UIPageViewController.GestureRecognizers and assign self as a delegate for those gesture and implement
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
Your code may be like this:
In viewDidLoad
for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers)
{
gesRecog.delegate = self;
}
And add the following method:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (touch.view != self.pageViewController.view]
{
return NO;
}
return YES;
}
The documented way to prevent the UIPageViewController from scrolling is to not assign the dataSource property. If you assign the data source it will move into 'gesture-based' navigation mode which is what you're trying to prevent.
Without a data source you manually provide view controllers when you want to with setViewControllers:direction:animated:completion method and it will move between view controllers on demand.
The above can be deduced from Apple's documentation of UIPageViewController (Overview, second paragraph):
To support gesture-based navigation, you must provide your view controllers using a data source object.

How do I make the keyboard go away when a user clicks on the background of the view?

I have a UITextField in my iOS app. When a user enters text and clicks Return, the keyboard goes away due to a call to an IBAction with "resignFirstResponder."
However, XCode does not let me drag a line from the UIView itself to File Owner. How do I associate touching the background of a UIView with an IBAction that makes the keyboard go away?
You can use UITapGestureRecognizer. see: Dismiss keyboard by touching background of UITableView
so instead of tableview, just add it to your view instead:
UITapGestureRecognizer *gestureRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideKeyboard)] autorelease];
gestureRecognizer.cancelsTouchesInView = NO; //so that action such as clear text field button can be pressed
[self.view addGestureRecognizer:gestureRecognizer];
and have a method to hide your keyboard
- (void) hideKeyboard {
[self.view endEditing:YES];
}
You've already noticed that you can't drag from the UIView to the file's owner to assoctiate an action with a touch.
The way to work around this is to change the class of the background view from UIView to UIControl and hook up an action from there to a method in your controller to stop editing.
That's because a UIControl can respond to touch events, and a UIView does not, but a UIControl subclasses UIView, and so it can be used in place of a UIView.
I wrote an example project a while ago that uses this technique. Have a look at the secondViewController's xib file and see how I've change the class of the background view and hooked it up to a an action in the controller to dismiss the keyboard.
Use the touchesBegan with Event and end editing on the view:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
One easy way to do it is to create a big transparent UIButton behind the view.

UISplitViewController non root, forcing custom rotation methods, makes master view dissappear

I'm trying to add a split view inside of a tab bar, and since the split view isn't the root, it doesn't properly get the rotation notifications, so the delegate's methods are never called to add the button to the toolbar in the detail view.
I've rigged it up so I can generate the popover when rotated, but when this method is called, the view dissappears from the landscape mode, and if you activate it and then rotate back into landscape, it's a black empty box where the master view used to be. How do I get rid of this occuring?
-(void) displayPopover:(id)sender
{
//Toggle the popover: if it's showing, hide it
if (popoverController != nil && [popoverController isPopoverVisible])
{
[popoverController dismissPopoverAnimated:NO];
}
else
{
//Create a Popover displaying the master view
if (popoverController == nil)
{
popoverController=[[UIPopoverController alloc] initWithContentViewController:self->rootController];
popoverController.popoverContentSize=CGSizeMake(300, 500);
}
[popoverController presentPopoverFromBarButtonItem:[detailController.toolbar.items objectAtIndex:0] permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}
You will need to remove all the objects from window using:
[appdelegate window ] subviews] objectAtIndex:0] removeFromSuperview];
Then add your splitview to the window, you can get the view callbacks.
I would recommend either finding a way to get your SplitViewController to be root, or creating a custom subclass of the UISplitViewController that allows for non-root placement.
I really like what Matt Gemmell did here: http://mattgemmell.com/2010/07/31/mgsplitviewcontroller-for-ipad
Using a custom subclass like Matt's will allow you to benefit from all the same delegate methods that a SplitView as root would allow. I used it in a project where I wanted my SplitView to appear as a modal - almost impossible with a traditional UISplitViewController.
so your split view has rotation enabled (shouldAutorotateToInterfaceOrientation:) now you have to make sure that the tab controller has also rotation enabled (should be the appDelegate, am I right?) AND you have to make sure that every other view that is in your TabBar has also rotation enabled!
so if your TabBar contains 2 tabs you have to set the rotation in 3 classes.