I have a UIWebView that is drawn programmatically and gets allocated and displayed through multiple subviews (the webview gets added to the superview).
This all works, however I have one little problem:
If 2 different subviews display this webview then I get 2 webviews, so when 1 view dismisses the webview the other remains. I don't want this.
Originally I was thinking just implement the webview in the superview class, however it didn't work.
How can I have the web view check to see if there are more then one of itself?
A webview instance can only be added to the view hierarchy once. If you have two webviews visible on screen at once, they are two different instances. You should keep track of these instances that you add to the hierarchy and when one dismisses, remove all the instances you are tracking from their superview.
You can also crawl a view hierarchy and look for instances of UIWebView.
for (UIView *subView in [myView subviews]) {
if ([subView isKindOfClass:[UIWebView class]]) {
[subView removeFromSuperview];
}
}
mhm, no, wait, it's not so clear what you are meaning...
You say: "...there are more then one of itself" and "...gets allocated and displayed through multiple subviews"
You probably mean that you have 2 instances of "the same" UIWebView class, but then you should not consider them as "the same object" which lives in 2 different superviews... they are different objects, everyone has its own properties...
Or did i misunderstood?
So, if you meant as i said and you just want to control from a subView (mhm... or we should say from its UIViewController) if there are other views which use a UIWebView.
I'd probably use one UIViewController "parent" where to load my subViews (eventually they could also have their own UIViewController, then every time i Alloc and addSubview a UIWebView in my subView i just add a tag to it:
myWebView.tag = 11;
it could change if needed for next one...
the purpose is to be able to control if in my UIView there are allocated some UIWebView,
now we can do that with this in my main parent UIViewController:
for (UIView *view_level_1 in [self.view subviews]) {
for (UIView *view_level_2 in [view_level_1 subviews]) {
if (view_level_1 >= 10) {
// do something: dismiss this UIWebView too...
}
}
}
it just control in all the subViews of the view of my mainViewControl if there is a subView "tagged" before (a tag is a sort of "name/id")
I'm not sure the structure of your subView could be like that, it was not so clear in your question, but you can change the code adopting it...
luca
Use UIView's isDescendantOfView method to know if any subView is currently present on parentView.
if([addedSubView isDescendantOfView:parentView])
{
//addedSubView is subview of parentView
//Take necessary action.
}
else
{
//addedSubView is not subview of parentView
//Take necessary action.
}
Related
Periodically, I add a UIView to the UIDynamicAnimator, which has some behaviours of its own. But when I remove the UIView from it's superview (when it falls offscreen) the UIDynamicAnimator still keeps the UIView's behaviours in its 'behaviors' property.
My question is, exactly what is the best approach to handling the behaviours in a UIDynamicAnimator?
Do I have to manually keep track of all the behaviours pertaining to that UIView and manually remove them before removing the UIView from the view hierarchy?
[myBehavior removeItem:item] does not throw an exception if item is not part of myBehavior, so what you could do is have a generic removeView method that removes a view from all behaviors that could possibly pertain to it, something like:
- (void) removeView: (UIView *) view{
[_gravity removeItem: view];
[_collisions removeItem:view];
[_otherBehavior removeItem:view];
//and et cetra for all of your behaviors
[self.view removeItem: view];
}
Which could be called whenever you need to remove a view. Even if, say, the view is not part of _otherBehavior this method would still properly remove the view.
I have a ViewController in which the user selects a card (a custom UIButton) out of a UIScrollView. I have intercepted the touch event selecting the card and identified it, and then removed it from the data source, but it still exists in the UISubView. How do I get rid of it? Refreshing the view should show it removed from the view. How do I do that?
you can do it in one of two places:
in your viewcontroller
directly in the view
you need to call the function setNeedsDisplay
if you do it from the viewController then [yourViewOutletVariable/viewParameter setNeedsDisplay];
if you write it in the view itself then [self setNeedsDisplay];
hope this helps
You can either let view controller observe your models or update your views manually.
I'm not very clear about your question, what is still remaining on your view?
For automatically update views when model changes, I suggest ReactiveCocoa.
Once you have a handle on your view:
UIView *v = ...;
[v removeFromSuperview];
You could also call the setNeedsDisplay method on your scroll view after calling removeFromSuperview.
If your point is to refresh "UIViewController", then:
[self viewDidLoad];
I am trying to just do a simple view change for proof of concept.
here is the code
- (void)swipedScreen
{
if (self.secondView.superview == nil) {
[myView removeFromSuperview];
[self.view insertSubview:secondView atIndex:0];
}
}
when I swipe the screen what happens is the view area just goes black... and becomes unresponsive.
I started with a navigatoin app, replaced the tableview with just a standard uiviewcontroller class.. that worked fine..Then i added a secondView (xib only) and changed its class to match the viewcontroller of the first view.
The reason I am finding this difficult is because i am trying to animate the views inside the navigation controller and not push a whole view onto the stack which I am used to doing.
I'll bet that blank unresponsive view is, in reality, your secondView object. I always test by setting [secondView setBackgroundColor:[UIColor greenColor]] and checking if the massive green rectangle actually shows up.
EDIT: having looked at your code, there are multiple problems that arose:
You never actually +alloc or -init anything.
You never actually touch those nibs or make a reference to them in code
You declare two UIView's as IBOutlets and Strong (two exact opposites, as IBOutlets are __weak, __unsafe_unretained, or assign), yet do not link them to anything.
I've taken the liberty of revising it (sans nibs). Take a look.
Did you init the secondView? if init,you can try to set frame for the secondView
Your inserting the view at the bottom of the stack,
[self.view insertSubview:secondView atIndex:0];
Try using addSubview instead. Also you need to set the views frame somewhere.
Basically i have a WebView on SecondViewController and I wish for the WebView to be visible on every view like a tab bar and fully controllable on each view.
Please note the WebView will be on a webpage with a online slideshow so I cannot simply reload on each view
Also in the SecondViewController I have
- (void)webViewDidFinishLoad:(UIWebView *)YouTubePlayer {
I would suggest adding the webView on you window after the you add the tabbarcontroller.view just like:
[window addSubview:tabbarController.view];
[window addSubview:webview];
[window makeKeyAndVisible];
and initially make don't make it visible. You should handle all the webview related methods in the app delegate. Now whenever you don't need it you can hide it by calling the methods your wrote in app delegate from your view controllers.
Hope this helps.
I'd just set up a singleton UIWebView and add it to each view-controller-view when that view controller is about to become visible. Here's one way to do it:
//.h
#interface SharedWebView : UIWebView
{
}
+ (SharedWebView*) shared;
#end
//.m
SharedWebView* g_sharedWebView;
#implementation SharedWebView
+ (SharedWebView*) shared
{
if ( g_sharedWebView == nil )
{
g_sharedWebView = [[SharedWebView alloc] init];
// ... any other intialization you want to do
}
return g_sharedWebView;
}
#end
// in your view controller(s)
#property (readonly) UIWebView* webView
- (UIWebView*) webView
{
return [SharedWebView shared];
}
- (void) viewWillAppear: (BOOL) animated
{
[super viewWillAppear: animated];
[self.view addSubview: self.webView ];
self.webView.frame = CGRectMake(10, 10, 300, 300);
// want to re-set the delegate?
// self.webView.delegate = self;
}
The simplest approach is to just make all of your view controllers aware of this extra view (make the view available through a singleton PinnedViewController or whatever). During each view controller's -viewWillAppear, just do this:
[self addSubview:[[PinnedViewController sharedController] view]];
This will move the view to whoever is currently active (making a view your subview automatically removes you from your old hierarchy).
If that is cumbersome or otherwise unworkable, there are two other options. First, you can subclass UITabViewController (I assume that's what you're using here from your question), and inject your extra view (resizing the content view to make room). This is undocumented and unsupported, so take heed. But it's not incredibly difficult if you don't do too many other fancy tricks.
The other tricky solution is to create a second UIWindow that you float over the main UIWindow (or resize the main UIWindow to make room for it). This is only semi-documented and is also not really supported. But this approach can work if you're trying to put the extra view below the tabbar for instance.
But if your system is simple enough, I recommend just letting your view controllers all manage the pinned view manually. You'll save a lot of code spelunking that way, and you won't have to rely on any undocumented internal view hierarchies.
Sounds like you try to put views on top of this view but not modal. There was this blog entry I once saw that described how you would do something like this. I think it should apply also for your case: semi-modal-transparent-dialogs-on-the-iphone
In iOS UIViewControllers are expected to manage an entire "screen" worth of content so it's not normal to try to share a single view across many view controllers. Trying to have UIViewControllers whose views only manage part of their window is problematic and will result in unexpected behavior as UIKit will not send messages like -viewWillAppear to all view controllers with visible views. Instead you would normally create a single UIViewController whose view includes that web view and whatever other views compose your tab like interface.
Alternately you could have a hierarchy of many view controllers and add a single web view as a subview of all of them. You would then pull your web view delegate behavior out into some non-UIViewController controller class to manage the behavior of the web view.
You can have all your views take up a portion of the screen and have your UIWebView take up the rest. The logic for switching between the other views should remain the same.
For example, in your viewDidLoad method for your UIViewControllers, you could have something like:
self.view.frame = CGRectMake(0, 100, 320, 380);
And in your (say) AppDelegate, you would have your normal call to show the main UIViewController (in your case, it sounds like a UITabBarController?) and also have a call to add the UIWebView. Say, something like:
myWebView.view.frame = CGRectMake(0, 0, 320, 100);
The view controllers and UIWebView would be independent of each other.
You see this pattern, I believe, with apps that have iAds. I've done something just like this with one of my free apps.
Hope this helps!
I got the same effect by simply adding it to the navigationController itself(if you don't have one then just add it).
works great for me in one of my apps.
Could you not use two webviews on your application and simply change the uppermost webview with your more dynamic content?
In IB I created UIView inside of a UIScrollView. File's owner for both is a UIViewController named JLViewController. The UIView is wired to class BodyClock.
The BodyClock class draws a graph inside the view. It also creates several small views which act as touch hot spots. When a hot spot is touched it displays an informative alert with a button for more detail. I need to tell JLViewController to display the detailed information. I thought I could do this by making the ViewController the HotSpot's delegate. Being as how I am creating the hot spots in the BodyClock class, I can't figure out how to set the hot spot delegate to JLViewController. I am trying to do something like this..
//Code in BodyClock
//create the hot spot
id viewController = [self nextResponder];
HelpHotSpot *helpHotSpot = [[HelpHotSpot alloc] initWithFrame:CGRectMake(start_x, melatoninHeightEnd_y, 80, 40)];
helpHotSpot.delegate = viewController;
[viewController addSubview:helpHotSpot];
[helpHotSpot release];
//Code in the HotSpot after touch and request for more info
//notify JLViewController to display the details
if ([self.delegate respondsToSelector:#selector(hotSpotMore:)]) {
[self.delegate hotSpotMore:itemDetails];
}
Everything works except that respondsToSelector fails. If I NSLog viewController or self.delegate I get...
UIScrollView: 0x7443c20; frame etc...
I was expecting JLViewController: instead of UIScrollView: so I think this is the problem.
How do I set the delegate to the ViewController for these subviews?
Is it possible, or should I be using notification instead?
This:
id viewController = [self nextResponder];
isn't doing what you think it is doing. This doesn't get a view's controller but instead looks at the chain of responders and determines which is the next one for responding to events - these are typically views or controls (not to be confused with controllers). It appears that after your hot spot view, the scoll view is next.
You'll have to get the pointer to the controller correct. Maybe in your hot spot view you could add:
IBOutlet JLViewController *viewController;
And in interface builder connect this to your view controller.