I have a framework that creates some views, the app that uses the framework calls a method from it and pass in the current view controller, the framework then calls presentModalViewController to display a view.
It was working just fine with iOS 6.1 SDK but when I updated to Xcode 5 and iOS 7 SDK I don't see the modal view anymore, instead all I get is a blank screen.
EDIT
Heres some code:
The Framework is called "testityi"
testityi.m
#import "TestViewController.h"
#implementation testitiy
- (NSString*) sayHi : (NSString*) name {
return [NSString stringWithFormat:#"Hello %#", name];
}
- (void) displayView:(UIViewController *)parentController {
TestViewController* controller = [[TestViewController alloc] init];
[parentController presentViewController:controller animated:YES completion:nil];
}
TestViewController is simply a view with a label that says "View from framework"
The framework itself works fine, calling sayHi method works just fine.
The third party app has a view with a label and a button which calls sayHi method and then displayView method, heres the view controller code:
MainViewController.m
- (IBAction)buttonPressed:(id)sender {
testitiy* framework = [[testitiy alloc] init];
NSString* msg = [NSString stringWithFormat:#"Calling sayHi method on framework...\n result: %#", [framework sayHi:#"John"]];
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"sayHi method call" message:msg delegate:self cancelButtonTitle:#"Ok, show me the view" otherButtonTitles:nil, nil];
[alert show];
}
-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == [alertView cancelButtonIndex]) {
testitiy* framework = [[testitiy alloc] init];
[framework displayView:self];
}
}
The alert button action is also working correctly, I added a NSLog before and its working.
After clicking the alert button a view is presented but instead of containing the label "View from framework" I get a blank screen.
You can see the code on Github
EDIT 2
I got it... I wasn't calling initWithBundle on the ViewController from the framework, I added the a custom init method that calls:
framework: TestViewController.m
+ (NSBundle *)frameworkBundle {
static NSBundle* frameworkBundle = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
NSString* mainBundlePath = [[NSBundle mainBundle] resourcePath];
NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:#"testity.bundle"];
frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath];
});
return frameworkBundle;
}
- (id) initWithFramework {
NSBundle* bundle = [[self class] frameworkBundle];
self = [super initWithNibName:#"TestViewController" bundle: bundle];
return self;
}
And changed testitiy.m
- (void) displayView:(UIViewController *)parentController {
TestViewController* controller = [[TestViewController alloc] initWithFramework];
[parentController presentViewController:controller animated:YES completion:nil];
//[parentController.navigationController pushViewController:controller animated:YES];
}
And now its working...
I hope this helps someone else but I'm guessing it was a stupid mistake of mine.
Sorry for all the trouble and thanks for your time!
So after a while I finally understand the issue:
When using a custom framework, all resources like images and NIB files have to be manually included in the third-party app so that it has access to those files.
My problem was that I was including the resources (stored in a bundle) into the third-party app but the framework was trying to display the View based on its own resources, which the app couldn't access, for that reason I was getting a blank screen.
I just needed to tell the framework to use the included bundle into the third-party app to display that View (using the initWithNibName: Bundle method).
See EDIT 2 in the question to see the code that solved my problem.
Hope this helps someone. :-)
Related
I have a Mac Catalyst app that's essentially a one-window app, but I added multi-window support (using scenes) to allow opening a second window for one function, which a small portion of users will use. Now Apple has rejected the app because with multi-window support, the app doesn't quit when a user clicks the red button at the top of the main window. One solution is to provide a menu item to reopen it, but I think it would be more intuitive for users if the app simply quit as it did before.
I found a similar problem on the Apple forums and am trying to implement the provided solution. Using this tutorial that provides more setup instructions, I have added a macOS bundle as a new target, embedded that into the iOS target, and added this class to the bundle:
#import "AppKitBridge.h"
#implementation AppKitBridge
#synthesize application;
#synthesize window;
- (id)init {
NSLog(#"AppKitBridge init");
self = [super init];
self.application = [NSApplication sharedApplication];
self.window = [[self.application windows] firstObject];
if (self.window) {
self.application.delegate = self;
self.window.delegate = self;
} else {
NSLog(#"AppKitBridge error: window is nil");
}
return self;
}
- (void)test {
NSArray *windows = NSApplication.sharedApplication.windows;
for (NSWindow *window in windows) {
NSLog(#"AppKitBridge window: %#", window);
}
}
- (void)applicationDidUpdate:(NSNotification *)notification {
NSLog(#"AppKitBridge applicationDidUpdate");
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
NSLog(#"AppKitBridge applicationShouldTerminateAfterLastWindowClosed");
return TRUE;
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
NSLog(#"AppKitBridge applicationShouldTerminate");
return TRUE;
}
#end
Then in viewDidLoad of the initial view controller of the iOS app, I call this method to load the bundle:
- (void)enableAppKit {
NSString *pluginPath = [[[NSBundle mainBundle] builtInPlugInsPath] stringByAppendingPathComponent:#"AppKit.bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:pluginPath];
[bundle load];
NSObject *appKit = [[[bundle classNamed:#"AppKitBridge"] alloc] init];
[appKit performSelector:#selector(test) withObject:nil afterDelay:0];
}
When I run the app, the console shows the AppKitBridge init, AppKitBridge window and AppKitBridge applicationDidUpdate lines. So it seems like the overall setup is working. But when I click the red window button, it does not show the AppKitBridge applicationShouldTerminateAfterLastWindowClosed or AppKitBridge applicationShouldTerminate lines, and the app does not quit.
Should this do what I'm expecting, and if so, what am I missing in the setup?
The problem is this line:
NSObject *appKit = [[[bundle classNamed:#"AppKitBridge"] alloc] init];
Your appKit object is a local variable so your AppKitBridge instance goes out of existence one line later. You need this object to persist if it is to function as the app/window delegate. Assign it to an instance property of some persistent object.
Essentially I'm working with 3 view controllers.
Main view which starts a download. (Webview based which passes the download).
Modal download controller. (Tab based).
Downloader (HCDownload).
In the main view my download gets passed like so:
//Fire download
[activeDL downloadURL:fileURL userInfo:nil];
[self presentViewController:vc animated:YES completion:nil];
activeDL is initialized in viewDidLoad:
activeDL = [[HCDownloadViewController alloc] init];
If I removed the presentViewController, it still downloads, which is fine. Then i tap my Downloads button, it brings up the controller which defines the tabs like so:
center = [[CenterViewController alloc] init];
activeDL = [[HCDownloadViewController alloc] init];
completedDL = [[DownloadsViewController alloc] init];
activeDL.tabBarItem = [[UITabBarItem alloc] initWithTitle:#"Active Downloads"
image:nil //[UIImage imageNamed:#"view1"]
tag:1];
completedDL.tabBarItem = [[UITabBarItem alloc] initWithTitle:#"Completed Downloads"
image:nil //[UIImage imageNamed:#"view3"]
tag:2];
[self setViewControllers:[NSArray arrayWithObjects:activeDL, completedDL, nil]];
However, it is not passing the current active download. I don't know if it's a initialization problem, or my tab issue of showing the current download.
From his github, he suggests to get the current number of downloads is to call: dlvc.numberOfDownloads which for me would be
[activeDL numberOfDownloads].
I call this in the the Downloader viewWillAppear but nothing shows.
Does anybody has any suggestions or have worked with this controller?
Any help would be appreciated.
When you call:
activeDL = [[HCDownloadViewController alloc] init];
You are creating a new download controller, which has its own internal downloads array. This library, as written, has no way to pass this information from one HCDownloadViewController object to another.
Tying downloads to VC's like this will cause problems -- I recommend you rewrite this code to split that apart.
To hack around it, try to create just one HCDownloadViewController object and pass it around.
Ok so with the last comment of the other answer, "Make activeDL a member variable instead of a local variable.", got me Googling and with some tinkering and bug fixing along the way I managed to get it all up and running perfect.
I declared it all in my AppDelegate.
AppDelegate.h
#interface SharedDownloader : HCDownloadViewController <HCDownloadViewControllerDelegate>
+ (id)downloadingView;
#end
AppDelegate.m
static HCDownloadViewController *active;
#implementation SharedDownloader
+ (id)downloadingView {
if (active == nil)
active = [[HCDownloadViewController alloc] init];
return active;
}
#end
Calling to the class for downloading in my main view controller:
-(id)init{
activeDL = [SharedDownloader downloadingView];
return self;
}
//Spot where I fire the download
if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
//More code here
[activeDL downloadURL:fileURL userInfo:nil];
}
Lastly in my tab bar controller:
-(id)init {
activeDL = [SharedDownloader downloadingView];
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
activeDL.tabBarItem = [[UITabBarItem alloc] initWithTitle:#"Active Downloads" image:nil] tag:2];
}
I believe that's all of it. In any case, thanks to Lou Franco for pointing me in the right direction.
I've just implemented a commenting feature in my app. Ideally when someone leaves a comment, I'd like all notified people be able to swipe the push notification and open the app on that post.
I assume you want to open the concerned page directly. There are many ways to go about this, and it depends on how your app is laid out.
If you want to open an inner page upon app launch, you can programmatically trigger the segues that the user would otherwise need to make manually. (this ensures the back/home buttons work as opposed to loading the desired page directly).
Here's an excerpt from one of my own code, your use case may not be the same, but this is all i can do unless you give us more details.
- (BOOL) navigateToRespectiveSectionforPushNot:(NSDictionary*)pushNot
{
id rootVC = self.window.rootViewController;
NSLog(#"ROOT CLASS : %#", [rootVC class]);
if ([rootVC isKindOfClass:[SWRevealViewController class]])
{
NSLog(#"Root Class looking good... mission Navigate!!");
SWRevealViewController *homeVC = (SWRevealViewController*) rootVC;
NSString *category = [[pushNot objectForKey:pushPayloadKeyaps] objectForKey:pushPayloadKeyCategory];
NSString *subCat = [[pushNot objectForKey:pushPayloadKeyaps] objectForKey:pushPayloadKeySubCategory];
NSLog(#"category : %# , subcat : %#",category,subCat);
//The code for the page to which i'm supposed to navigate to is contained in the push notification payload
if ([category isEqualToString:pushCategoryItemChat])
{
[homeVC.rearViewController performSegueWithIdentifier:#"chatPush" sender:nil];
UINavigationController *nc = (UINavigationController*)homeVC.frontViewController;
NSLog(#"FrontView Class : %#",[nc.viewControllers[0] class]);
UITableViewController *tvc = (UITableViewController*)nc.viewControllers[0];
NSDictionary *send = #{chatPushTargetUserId:subCat,chatPushTargetUserName:#"",chatPushTargetUserImage:#""};
[tvc performSegueWithIdentifier:#"seguePushDemoVC" sender:send];
return YES;
}
//communityPush historyPush
else if ([category isEqualToString:pushCategoryItemCommunity])
{
if ([subCat isEqualToString:pushSubCatItemNewRequest])
{
[homeVC.rearViewController performSegueWithIdentifier:#"communityPush" sender:nil];
return YES;
}
else if ([subCat isEqualToString:pushSubCatItemAccepted])
{
[homeVC.rearViewController performSegueWithIdentifier:#"communityPush" sender:nil];
return YES;
}
}
else if ([category isEqualToString:pushCategoryItemHistory])
{
[homeVC.rearViewController performSegueWithIdentifier:#"historyPush" sender:nil];
return YES;
}
}
else
{
UIAlertView *whoa = [[UIAlertView alloc] initWithTitle:#"WHOA!!" message:#" That wasn't supposed to happen. You are not even logged in. Call 911..." delegate:nil cancelButtonTitle:#"mmKay.." otherButtonTitles:nil, nil];
[whoa show];
}
return NO;
}
I hope the code is self explanatory. cheers
I am trying to load a UIImage View into a tabbar application with a global class, the image doesn't load the image the first time the rootviewcontroler is loaded, but it does show the alert. If you click on another tabbar item to load another view and come back to the first view the alert and image both show up correctly.
#import "globalNetworkCheck.h"
#import "CheckNetworkAvaliblity.h"
#import "ViewController.h"
#implementation globalNetworkCheck
static globalNetworkCheck *getNetworkStatus = nil;
static UIView *viewNetworkError = nil;
+(globalNetworkCheck *)getNetworkStatus
{
#synchronized(self)
{ if(getNetworkStatus==nil) {
// getNetworkStatus= [globalNetworkCheck new];
if ([CheckNetworkAvaliblity CheckNetwork])
{
{
[(UIView *)viewNetworkError removeFromSuperview];
viewNetworkError.backgroundColor=[UIColor clearColor];
viewNetworkError.hidden=YES;
[viewNetworkError removeFromSuperview];
[[viewNetworkError subviews]
makeObjectsPerformSelector:#selector(removeFromSuperview)];
NSLog(#"Success, Your network is available");
}
}
else
{
UIImageView *imgNoConnection = [[UIImageView alloc] initWithFrame:CGRectMake(0, 70, 320 , 449)];
viewNetworkError = [[UIView alloc]init];
[imgNoConnection setImage:[UIImage imageNamed:#"Noconnection.png"]];
viewNetworkError.frame = CGRectMake(0, 0, 320 , 449);
[viewNetworkError addSubview: imgNoConnection];
viewNetworkError.hidden = NO;
[[UIApplication sharedApplication].keyWindow.rootViewController.view addSubview:viewNetworkError];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle: #"Connection Failed"
message: #"You are not connected to the internet. Please check your network settings and try again."
delegate: self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
viewNetworkError.hidden = NO;
NSLog(#"Sorry, Network connection is unavailable");
}}}
return getNetworkStatus;
}
#end
I am calling the class method on
-(void)viewWillAppear:(BOOL)animated {
globalNetworkCheck *obj=[globalNetworkCheck getNetworkStatus];
Remove this line:
[viewNetworkError addSubview:viewNetworkError];
Strange to me, how are you going to add viewNetworkError to viewNetworkError.
Edit
I think you mean
[self addSubview:viewNetworkError];
You are going to need somewhere to add the view. Right now this class method has no association on the view hierarchy. If you want to keep it a class method like this then you'll either need to add your error views to the window or the root view controllers view.
[[UIApplication sharedApplication].keyWindow.rootViewController.view addSubview:viewNetworkError];
or
[[UIApplication sharedApplication].keyWindow addSubview:viewNetworkError];
Without understanding the full view stack it is a bit hard to answer further. Alternatively with more rework you can put this responsibility on the AppDelegate rather than a class method like this. Create a view that your App delegate can add to the hierarchy as needed, create a public getter on your app delegate to check current network status, etc. Here is an example of how to great a HUD type view:
http://blog.mugunthkumar.com/coding/ios-code-tweetbot-like-alertpanels/
I´m trying to integrate ShareKit in my ios game.
Everything is working fine and the actionsheet is shown and I can interact with it but I´m not able to return the focus to my app when the sharekit action has finished (by closing the actionsheet or finishing any action).
I have tried in several ways but any has worked for me. What´s happening?
I´m not an expert programmer so I expect I´m missing something.
I´m
This is my .h
#import <UIKit/UIKit.h>
#import "SHK.h"
#import "SHKConfiguration.h"
#interface SocialWrapper: UIViewController{
}
- (id) init;
- (void) open;
- (void) dealloc;
#end
and .m
#import "SocializeWrapper.h"
#implementation SocialWrapper
- (id) init {
self=[super init];
DefaultSHKConfigurator *configurator = [[DefaultSHKConfigurator alloc] init];
[SHKConfiguration sharedInstanceWithConfigurator:configurator];
[SHK flushOfflineQueue];
return self;
}
- (void) open
{
NSString *someText = #"Hello Earth!";
SHKItem *item = [SHKItem text:someText];
// Get the ShareKit action sheet
SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item];
UIWindow *window = [UIApplication sharedApplication].keyWindow;
[window addSubview:self.view];
[SHK setRootViewController:self];
[actionSheet showInView:self.view];
}
- (void) dealloc {
NSLog(#"SHK dealloc");
[self release];
[super dealloc];
}
#end
I´m calling it by using this wrapper
#import "SocializeWrapper.h"
SocialWrapper *socialize;
void SHKinit(void) {
NSLog(#"SHK Init");
socialize = [[SocialWrapper alloc] init];
}
void SHKopenWeb(void){
NSLog(#"SHK Open actionsheet");
[socialize open];
}
I´m working with ios 5, xcode 4.3.2 and the last sharekit version from the git.
I think I have to dissmiss my SocialWrapper once the actionsheet is closed but I don´t know how to capture that event, or even if this is correct. I´m stucked.
any help will be greatly appreciated.
UPDATE
As comment adviced, now the controller is on a category, using the actionsheet delegate, the focus can be regained when clicking the cancel´s actionsheet button. The problem still persists when an action is finished or cancelled. Don´t know how to capture that event.
This is my category code:
#import "SocialWrapper.h"
#implementation UIViewController (SocialController)
-(void) loadconfig
{
DefaultSHKConfigurator *configurator = [[DefaultSHKConfigurator alloc] init];
[SHKConfiguration sharedInstanceWithConfigurator:configurator];
[SHK flushOfflineQueue];
}
- (void) open
{
NSLog(#"Opening social button");
NSString *someText = #"Monkey Armada rules!";
SHKItem *item = [SHKItem text:someText];
// Get the ShareKit action sheet
SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item];
UIWindow *window = [UIApplication sharedApplication].keyWindow;
[window addSubview:self.view];
[actionSheet setDelegate:self];
[SHK setRootViewController:self];
[actionSheet showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSLog(#"SHK actionsheet dissmiss with button %d", buttonIndex);
if(buttonIndex == 4)
{
NSLog(#"SHK close actionsheet");
[self dismissViewControllerAnimated:YES completion:nil];
[self.view removeFromSuperview];
}
}
#end
Well since SHKActionSheet is a subclass of UIActionSheet you can set the delegate of that class to self to know when the dismissal happens.
Also, [self release]; in dealloc is complete misunderstanding of what release does, if you're in dealloc then releasing self won't do anything !
Learn the memory management rules.
I should also warn you that [window addSubview:self.view] is deprecated, you should not do that at all. In fact, I don't see a reason to wrap share kit stuff each view controller should be able to write that code easily. At worse you could put that code in a category on UIViewController if you don't want to rewrite the code every time.