UIView did appear? - iphone

I'm wondering, is there a way to get a delegate or something, when a particular UIView has been shown on the screen ?

Swift version. Inside your UIView class just:
override func willMove(toWindow newWindow: UIWindow?) {
super.willMove(toWindow: newWindow)
if newWindow == nil {
// UIView disappear
} else {
// UIView appear
}
}

Try these:
– didAddSubview:
– willRemoveSubview:
– willMoveToSuperview:
– didMoveToSuperview
– willMoveToWindow:
– didMoveToWindow
- viewDidAppear:

If you manage your logic directly inside the UIView, use:
- didMoveToSuperview
If you manage your logic inside a UIViewController, use :
- viewDidAppear:(BOOL)animated

If you are managing the UIView via a UIViewController, then you can use the -viewDidAppear: method:
- (void) viewDidAppear:(BOOL) animated {
//do stuff...
[super viewDidAppear:animated];
}

Another way to find out when a control is on screen is to subclass the View or Control and override drawRect...
However, it's called when it's drawn and not only when first shown. So it's only sometimes what you want. It worked for my case. Make sure to call super as well! =)

Related

Where to put the UIAlert? [duplicate]

I want to check the pasteboard and show an alert if it contains specific values when the view appears. I can place the code into viewDidLoad to ensure it's only invoked once, but the problem is that the alert view shows too quickly. I know I can set a timer to defer the alert's appearance, but it's not a good work-around I think.
I checked the question iOS 7 - Difference between viewDidLoad and viewDidAppear and found that there is one step for checking whether the view exists. So I wonder if there's any api for doing this?
Update: The "only once" means the lifetime of the view controller instance.
There is a standard, built-in method you can use for this.
Objective-C:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([self isBeingPresented] || [self isMovingToParentViewController]) {
// Perform an action that will only be done once
}
}
Swift 3:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if self.isBeingPresented || self.isMovingToParentViewController {
// Perform an action that will only be done once
}
}
The call to isBeingPresented is true when a view controller is first being shown as a result of being shown modally. isMovingToParentViewController is true when a view controller is first being pushed onto the navigation stack. One of the two will be true the first time the view controller appears.
No need to deal with BOOL ivars or any other trick to track the first call.
rmaddy's answers is really good but it does not solve the problem when the view controller is the root view controller of a navigation controller and all other containers that do not pass these flags to its child view controller.
So such situations i find best to use a flag and consume it later on.
#interface SomeViewController()
{
BOOL isfirstAppeareanceExecutionDone;
}
#end
#implementation SomeViewController
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if(isfirstAppeareanceExecutionDone == NO) {
// Do your stuff
isfirstAppeareanceExecutionDone = YES;
}
}
#end
If I understand your question correctly, you can simply set a BOOL variable to recognize that viewDidAppear has already been called, ex:
- (void)viewDidAppear {
if (!self.viewHasBeenSet) { // <-- BOOL default value equals NO
// Perform whatever code you'd like to perform
// the first time viewDidAppear is called
self.viewHasBeenSet = YES;
}
}
This solution will call viewDidAppear only once throughout the life cycle of the app even if you create the multiple object of the view controller this won't be called after one time. Please refer to the rmaddy's answer above
You can either perform selector in viewDidLoad or you can use dispatch_once_t in you viewDidAppear. If you find a better solution then please do share with me. This is how I do the stuff.
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(myMethod) withObject:nil afterDelay:2.0];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
static dispatch_once_t once;
dispatch_once(&once, ^{
//your stuff
[self myMethod];
});
}
By reading other comments (and based on #rmaddy 's answer), I know this is not what OP asked for, but for those who come here because of title of the question:
extension UIViewController {
var isPresentingForFirstTime: Bool {
return isBeingPresented() || isMovingToParentViewController()
}
}
UPDATE
You should use this method in viewDidAppear and viewWillAppear. (thanks to #rmaddy)
UPDATE 2
This method only works with modally presented view controllers and pushed view controllers. it's not working with a childViewController. using didMoveToParentViewController would be better with childViewControllers.
You shouldn't have issues in nested view controllers with this check
extension UIViewController {
var isPresentingForFirstTime: Bool {
if let parent = parent {
return parent.isPresentingForFirstTime
}
return isBeingPresented || isMovingFromParent
}
}
Try to set a BOOL value, when the situation happens call it.
#interface AViewController : UIViewController
#property(nonatomic) BOOL doSomeStuff;
#end
#implementation AViewController
- (void) viewWillAppear:(BOOL)animated
{
if(doSomeStuff)
{
[self doSomeStuff];
doSomeStuff = NO;
}
}
in somewhere you init AViewController instance:
AddEventViewController *ad = [AddEventViewController new];
ad.doSomeStuff = YES;
Not sure why you do this in ViewDidAppear? But if you want doSomeStuff is private and soSomeStuff was called only once, here is another solution by notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSomeStuff) name:#"do_some_stuff" object:nil];
- (void) doSomeStuff
{}
Then post when somewhere:
[[NSNotificationCenter defaultCenter] postNotificationName:#"do_some_stuff" object:nil];
swift 5
I've tried isBeingPresented() or isMovingToParent.
But It doesn't work.
So I tried below code. and It's work for me!
override func viewDidAppear(_ animated: Bool) {
if (self.isViewLoaded) {
// run only once
}
}
You can use this function in ViewDidLoad method
performSelector:withObject:afterDelay:
it will call that function after delay. so you don't have to use any custom timer object.
and For once you can use
dispatch_once DCD block.Just performSelector in the dispatch_once block it will call performSelector only once when ViewDidLoad is called
Hope it helps

Accessing to UIScrollView from other viewController

I have two viewController:
AController (UIScrollView is subview) and BController
from AController I using presentModalViewController to BController . From BController i using dismissModalViewControllerAnimated to back to AController. But, i want set hidden for the UIScrollView from BController.
Please help me !
Thanks!
Use delegation, if you dont have so much expariance use in your AView viewWillAppear notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideScrollView) name:#"hideScrollView" object:nil];
and in your BView use:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"hideScrollView" object:nil];
In result i will fire hideScrollView method in AView when you can hide your scrollView.
I think the cleanest way to do this is to use a delegation protocol. AController will be the delegate of BController. And before BController is dismissed, it can call a method of the protocol to alert AController, and AController itself while hide the scrollView.
Alternatively, you can override -viewWillAppearin the AController implementation file, to test if the 'self.presentedController' is a subclass of BController. If it is, you can hide the scrollView.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if ([self.presentedViewController isKindOfClass:[BController class]]) {
//hide the scroll view
}
}
Also, I would suggest that you don't use -dismissModalViewControllerAnimated: unless you want to support iOS 4 and earlier: this method has been deprecated in iOS 6 and you'd better use - dismissViewControllerAnimated:completion: right away.

Detect scrolling in Subclass of UIScrollView

Good morning,
I've creates a Subclass of UIScrollView and I now want to know when the user is scrolling in my subclass. For that I implemented it like the following:
ImageScroller.h
#interface UIImageScroller : UIScrollView <UIScrollViewDelegate> {
ImageScroller.m (within the #implementation)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(#"did scroll");
}
The problem is, that the method scrollViewDidScroll doesn't seem to get fired.
Is there any possibility to get it to work?
I also tried to set the delegate to it self, but it doesn't work.
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
self.directionalLockEnabled = YES;
[self setDelegate:self];
}
return self;
}
I added a ScrollView to my XIB-File and set the Class if it to my ImageScroller. Also I've set the Fileowner and I'm using the UIScrollViewDelegate in the .h-File of the ViewController as well as implementing the Method scrollViewDidScroll in the .m-file.
When I set the delegate of my ImageScroller in the code of the .m-file from the XIB like
[imageScroller setDelegate:imageScroller]
the scrollViewDidScroll is fired in my ImageScroller-Subclass, but the one in my ViewController isn't fired, but I need both.
Any solutions for that?
Thanks for your answers in advance.
I ran into the same problem creating a subclass of the UIScrollView but solved it this way. As mentioned above, you can set yourself (subclass instance) as the delegate however this is what I did.
In the sub class I overrode the following methods
//Override this to detect when the user is scrolling, this is also triggered during view
//layout. Here you can check your deceleration rate and determine when the scrolling has
//grinded to a halt.
-(void)setContentOffset:(CGPoint)contentOffset
//Override this to detect when the view has been resized (just a handy override)
-(void)setFrame:(CGRect)frame;
As far as the other UIScrollViewDelegate methods, the View Controller should be responsible for handling those in my opinion.
I think you can try setting self.delegate = self; to receive events.
If anyone is looking for a Swift answer, it's as simple as this:
override var contentOffset: CGPoint {
didSet {
if contentOffset != oldValue {
//same as scrollViewDidScroll
}
}
}

Can't resignFirstResponder with UITextView

I have a UITextView. I implemented a navigationBar UIBarButtonItem to respond to a touch and resign the firstResponder for my UITextView.
But, when the selector method is called, the keyboard doesn't get dismissed. I checked the UITextView's responder status with isFirstResponder and it returns YES. I also checked it with canResignFirstResponder and the return value is NO.
I must be missing something here...why is it returning NO?
I get that I can override canResignFirstResponder by subclassing UITextView, but I'd like to avoid that if possible.
Here's a code snippet:
- (void) commentCancelButtonTouched:(id)sender
{
NSLog(#"Cancel button touched");
[self.navigationBar popNavigationItemAnimated: NO];
if ([self.textInput.textView canResignFirstResponder] == NO) {
NSLog(#"I don't want to resign!");
}
[self.textInput.textView resignFirstResponder];
}
Just in case anyone wants to hide the keyboard when you touch outside of the textview, it's pretty easy...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self.xFront resignFirstResponder];
}
xFront is my outlet to my UITextView.
Addressing your UITextField by way of a UIView subclass is a little non-standard. If I were you I'd get myself a direct handle on that UITextField right from inside your view controller. Set it up as a synthesized property of your view controller, give it the keyword IBOutlet so you can talk about it in Interface Builder, then make sure the outlet is hooked up to the text field.
In other words, rather than talking about self.textInput.textView, you want to be talking about self.textView directly. And double-check your IB outlet hookup, because I suspect that's where the real trouble is.
I figured it out. I had a 'UITextViewDelegate' method in my 'textInput' view, textViewShouldEndEditing that was overrided to return NO. Changed it to YES and it solved the problem.

How to respond to touch events in UIWindow?

Is it possible to handle touch events in the key UIWindow in the app Delegate or anywhere else?
Any help would be appreciated please.
There is a handy catch-all method in UIWindow called sendEvent: which sees every event near the start of the event-handling pipeline. If you want to do any non-standard additional event handling, this is a good place to put it. Something like this:
- (void)sendEvent:(UIEvent *)event {
if ([self eventIsNoteworthy:event]) [self extraEventHandling:event];
[super sendEvent:event]; // Apple says you must always call this!
}
Docs: UIWindow class reference | iOS event delivery docs
This blog post also mentions how to override hitTest:withEvent: to catch some events before they're bound to the target leaf subview in the view hierarchy. You can also override that method on your UIWindow object if you want.
You will have to subclass UIWindow with your own class and override sendEvent: method. But remember that the method gets other types of events - not only touches so you have to check for event type (event.type == UIEventTypeTouches). Also since you get set of touches you might want to check which ones just began, which ones ended, moved etc. To do that you have to iterate through allTouches and check the phase property of every UITouch.
#implementation TouchWindow
- (void)sendEvent:(UIEvent *)event {
if (event.type == UIEventTypeTouches) {
for(UITouch * t in [event allTouches]) {
if(t.phase == UITouchPhaseBegan) {
/*
Paste your code here.
Inform objects that some touch has occurred.
It's your choice if you want to perform method/selector directly,
use protocols/delegates, notification center or sth else.
*/
}
}
}
[super sendEvent:event];
}
#end
Of course TouchWindow is subclass of UIWindow
#interface TouchWindow : UIWindow
#end
And you will probably have to change that class in your .xib file in XCode
UIWindow is a subclass of UIView, so you simply subclass it and use it in you AppDelegate:
self.window = [[MyWindow alloc] initWithFrame:CGRectMake(0,0,320,480)]; // Sorry for hard-coded frame size!
and in MyWindow you override -hitTest:withEvent: and/or -pointInside:withEvent:
UIWindow is a subclass of UIResponder, which has APIs for handling touch events (e.g., touchesBegan:withEvent:). It is possible then for you to subclass UIWindow, override the touch event handling APIs, and manage the touch events yourself.