I'm just starting to learn OSX Cocoa app development. I would like to display a website inside a native OSX window. I thought a WebView would be the right way. I would like the webview to always take up 100% of the containing windows' size.
After struggling a bit, I understand how to catch the 'window resize' event, but I have no clue how to resize the web view according to the windows new size.
Here's what I have so far:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>
#interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet WebView *websiteWebview;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (NSSize) windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
{
WebView *view = [self websiteWebview];
[view setFrame:CGRectMake(0, 0, 1000, 1000)];
return frameSize;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[self window] setDelegate:self];
NSURL *url = [[NSURL alloc] initWithString:#"http://conradk.com"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[[[self websiteWebview] mainFrame] loadRequest:request];
}
#end
I thought calling [view setFrame:CGRectMake(0, 0, 1000, 1000)] would resize the web view as well, but it seems to not be the case.
Any tips / hints please? Is a WebView the right way to do this? Thanks for your help!
You need to make your WebView part of the window's contentView.
[self.window setContentView:self.websiteWebview];
By default, this will let the webView auto-resize with the window. You'll only need to mess with the sizing if you want the webview to do something other than match the size of the window.
Related
I'm very new to programming and objective c so please go easy on me.
I would like a UIButton (which I'm using as an IBAction) to change an the image in a UIImageView when pressed. I put the UIImageView into a UIScrollView in the storyboard but I hope I can still programmatically change the image.
I have searched absolutely everywhere for an answer for hours but nothing has worked for me either because it wasn't the right solution or I didn't do it properly.
I have tried this code and some more but they always return a "Thread 1: signal SIGABRT" when I press the button:
imageView.image = [UIImage imageNamed: #"Ruler pic inch.png"];
Here is my code so far:
ViewController.h
#import <UIKit/UIKit.h>
#import <iAd/iAd.h>
#interface ViewController : UIViewController <ADBannerViewDelegate, UIScrollViewDelegate> {
ADBannerView *adView;
BOOL bannerIsVisible;
IBOutlet UIScrollView *scrollView;
IBOutlet UIImageView *imageView;
IBOutlet UIButton *proVersion;
IBOutlet UIButton *howToUse;
}
- (IBAction)switchUnit:(id)sender;
#property (nonatomic, assign) BOOL bannerIsVisible;
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
#property (nonatomic, retain) IBOutlet UIImageView *imageView;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize bannerIsVisible;
#synthesize scrollView;
#synthesize imageView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Hide status bar:
[[UIApplication sharedApplication] setStatusBarHidden:YES];
// iAd:
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.frame = CGRectOffset(adView.frame, 0, -50.0f);
[self.view addSubview:adView];
adView.delegate=self;
self.bannerIsVisible=NO;
// Setting scrollview content size to size of image
scrollView.contentSize = CGSizeMake(320,2246);
}
-(void)bannerViewDidLoadAd:(ADBannerView *)banner
{
if (!self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOn" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, 50.0f);
[UIView commitAnimations];
self.bannerIsVisible = YES;
}
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOff" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, -50.0f);
[UIView commitAnimations];
self.bannerIsVisible = NO;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)switchUnit:(id)sender {
// CODE THAT MAKES IMAGE CHANGE GOES HERE (I thought...)
}
#end
Help would be greatly appreciated. I'm fed up with it. I've probably done a silly mistake somewhere but I can't work it out. Thanks in advance.
General pointers:
Try to use #property for all your instance variables (it'll server you well in future)
Don't use #synthesize (the compiler does a better job of it for you)
If you have a property bob, access it by using self.bob
Turn on ARC (looks like you don't have it on currently)
Best not to have any spaces in image names
To be clear on properties, when you have a property you don't need to create instance variables in between {} of the #interface. These should be removed in order to prevent multiple definitions. The auto-synthesising done by the compiler will define the instance variables for you but you should always use the property to access the value (as in item 3 above).
That said, your - (IBAction)switchUnit:(id)sender looks fine, if a little empty. The code should be something like:
self.imageView.image = [UIImage imageNamed:#"Ruler_pic_inch.png"];
For it to work, the image Ruler_pic_inch.png would need to be in your bundle (which means it's in Xcode project and set to copy during the build.
Again, that said, your problem would seem to relate to you being confused somewhere. SIGABRT type issues would usually be picked up by the compiler and notified to you as a warning saying something like "some object may not respond to some selector". If you see any of those, look at them and work out why you're trying to ask the object a question it doesn't understand.
If you don't see any compiler warnings, then you've told the compiler that an object is going to be of one type and then actually set it to something else. In this case I'd look at your IBOutlets in Storyboard and check that they are actually set to the correct destination view (disconnect and reconnect them all to be sure).
I have spent about a whole day of grief trying to solve this question, and this is my last resort. I have a web view in my OS X app. I made a web browser and it is working great with navigation arrows and all, but when I try to make a home page (google) It does not load on the WebView (called webber in the code). I made sure all the outlets are connected. here is my code for AppDelegate.h:
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>{
NSWindow *window;
NSTextField *urlBox;
IBOutlet WebView *webber;
IBOutlet NSTextField *googleSearchField;
}
-(IBAction)search;
#property (assign) IBOutlet NSWindow *window;
#end
and here is the code for AppDelegate.m:
#import "AppDelegate.h"
#implementation AppDelegate
- (void)windowControllerDidLoadNib:(NSWindowController *)windowController{
NSLog(#"Nib loaded");
NSString *urlText = #"http://www.google.com";
[[webber mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlText]]];
}
-(IBAction)search
{
NSString *searchString = [googleSearchField stringValue];
NSString *searchURL = [NSString stringWithFormat:#"http://www.google.com/search?q=%#", searchString];
[[webber mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: searchURL]]];
}
#end
I would very much appreciate ANY form of help and I am very desperate. Please remember that I am only in 6th grade, and a step by step guide or just plain code is what I am looking for. Thank you for your time! download the Xcode project here: http://www.cadenfarley.com/cobra/Download.html Scroll down until you see "Xcode project here"
Your windowControllerDidLoadNib: method is never called because you don't have an NSWindowController anywhere. Rename that method to - (void)awakeFromNib and it should work.
I have 10 different pages built into a slider control in my app, these pages are setup using storyboards.
The only difference on each page is a different web view to display rich text and a different image as a background are used.
Is it possible for me to have one view controller for all 10 pages and setup some flags in the constructor which would be executed on every page when its loaded to tell it what image and web view to show? If so what would this look like?
Thanks,
Lewis.
You can create subclass of UIViewController, for example MyViewController.
And then replace your .h file with:
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController
#property (nonatomic, strong) UIWebView *myWebView;
#property (nonatomic, strong) UIImageView *myImageView;
- (id)initWithURLString:(NSString *)urlString image:(NSString *)imageName;
#end
And your .m file with:
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
#synthesize myWebView = _myWebView;
#synthesize myImageView = _myImageView;
- (id)initWithURLString:(NSString *)urlString image:(NSString *)imageName
{
self = [super init];
if (self) {
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
_myWebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 400)];
[_myWebView loadRequest:urlRequest];
_myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
_myImageView.frame = CGRectMake(0, 400, 60, 60);
}
return self;
}
#end
And then just create an instances of your UIViewController subclass:
MyViewController *vCon = [[MyViewController alloc] initWithURLString:someURLString image:imageName];
I think the best approach would be to define a data source for your view controller. This data source would have the following interface, e.g.:
#protocol MyVCDataSource
- (NSUInteger)numberOfPages;
- (NSString*)htmlContentForPageIndex:(NSUInteger)index;
- (NSString*)backgroundForPageIndex:(NSUInteger)index;
#end
You would provide your view controller with a member called datasource that would be initialized in the initWithDatasource method:
#interface MyVC : UIViewController
...
#property (nonatomic, weak) id<MyVCDataSource> datasource;
- (id)initWithDatasource:(id<MyVCDataSource>)ds;
Then your view controller would just ask the data source for the HTML data or the background file name when it needs it:
- (void)viewDidLoad {
self.view.backgroundColor = GET_BACKGROUND_FROM_STRING([self.datasource backgroundForPageIndex:self.currentIndex]);
[self.webView loadHTMLString: [self.datasource htmlContentForPageIndex:self.currentIndex]];
....
}
I assumed that the protocol just returns strings, but indeed you can have it return what you need (e.g., an image, a color, an URL), it all depends on the internals of your class.
Finally, your datasource object could be any object (even your MyVC instance) and return its data by indexing into an array:
- (NSString*)htmlContentForPageIndex:(NSUInteger)index {
return [self.htmlPages objectAtIndex:index];
}
etc.
I'm using the latest Xcode (4.4.1) and developing for iOS 5.1. I am utilizing the bottom tab bar interface provided by Apple. One of the tabs uses a UIWebView that utilizes the full screen space. When I try to add a standard banner provided by AdMob, it does not add a banner at all. I was following along with: https://developers.google.com/mobile-ads-sdk/docs/admob/fundamentals. Code attached below
About.h
#import <UIKit/UIKit.h>
#import "GADBannerView.h"
#interface About : UIViewController <UIWebViewDelegate> {
IBOutlet UIWebView *webView;
// Declare one as an instance variable
GADBannerView *bannerView_;
}
#property (nonatomic, retain) UIWebView *webView;
#end
About.m
#import "About.h"
#import "GADBannerView.h"
#import "GADRequest.h"
#import "constants.h"
#implementation About
#synthesize webView;
//#synthesize bannerView = bannerView_;
+ (void)initialize {
// Set user agent (the only problem is that we can't modify the User-Agent later in the program)
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:UserAgent, #"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSString *fullURL = ([IsBeta isEqualToString: #"true"]) ? #"http://beta.wouldyouratherapp.com/questions/index/0/1" : #"http://wouldyouratherapp.com/questions/index/0/1";
NSURL *url = [NSURL URLWithString:fullURL]; NSURLRequest *requestObj = [NSURLRequest requestWithURL:url]; [webView loadRequest:requestObj];
// Create a view of the standard size at the bottom of the screen.
// Available AdSize constants are explained in GADAdSize.h.
bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner];
// Specify the ad's "unit identifier." This is your AdMob Publisher ID.
bannerView_.adUnitID = MyAdUnitID;
// Let the runtime know which UIViewController to restore after taking
// the user wherever the ad goes and add it to the view hierarchy.
bannerView_.rootViewController = self;
[self.view addSubview:bannerView_];
// Initiate a generic request to load it with an ad.
[bannerView_ loadRequest:[GADRequest request]];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
#end
Yes, I have added all the frameworks already, and MyAdUnitID is already defined in the constants file, so everything SHOULD be working, but I guess I am missing something. Any help?
If you're adding the bannerView_, you'll have to decrease the height of your webView accordingly to make room for the bannerView_. Since the origin of the ad looks like its at (0,0), you probably want something similar to this in your adView:DidReceiveAd: callback:
webView.frame = CGRectMake (0, bannerView_.frame.size.height, webView.frame.size.width, webView.frame.size.height - bannerView_.frame.size.height);
I've got the following method on a GameScreen.m file, with its own declaration - (void) drawNumbers on a GameScreen.h file:
//GameScreen.h
#import <UIKit/UIKit.h>
#interface GameScreen : UIView
{
IBOutlet UIButton *cell00;
}
- (void) drawNumbers;
- (IBAction) onCellClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *cell00;
#end
//GameScreen.m
#import "GameScreen.h"
- (void) drawNumbers
{
//testing if this works, so far it doesn't
[cell00 setTitle:#"Whatever" forState:UIControlStateNormal];
[cell00 setTitle:#"Whatever" forState:UIControlStateHighlighted];
}
I'm trying to call this method from my GameScreenViewController.m file, this way:
//GameScreenViewController.m
#import "GameScreenViewController.h"
#import "GameScreen.h"
...
- (void) viewDidLoad
{
GameScreen *aGameScreen = [[GameScreen alloc] init];
[aGameScreen drawNumbers];
[aGameScreen release];
[super viewDidLoad];
}
This is supposed to change the title of a button in a GameScreen.xib file where GameScreenViewController.m is the viewController and GameScreen class is the event handler where I get all the button clicks, timers running, etc. I am trying to call [drawNumbers] from [viewDidLoad] since I want the title to be changed when the screen is brought up front (screen management is done through the AppDelegate files).
The thing is, if I call drawNumbers instance from inside the same class through
//GameScreen.m
#import GameScreen.h
-(void) onButtonClick:(id)sender
{
//some other code
[self drawNumbers];
}
it works (as to say, nothing wrong with the code implementation or the graphic interface).
I've browsed through Apple Guide and tons of pages on the Internet, but I can't seem to find any light to this. Any further help (including answers as to where exactly find the answer in the ADG) would be really appreciated.
(Edited: here goes the AppDelegate code to flip to the specific view, just in case):
//myAppAppDelegate.h
#import <UIKit/UIKit.h>
#class myAppViewController, GameScreenViewController;
#interface myAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
myAppViewController *viewController;
GameScreenViewController *gameScreenViewController;
}
- (void) flipToGameScreen;
#property (nonatomic, retain) UIWindow *window;
#property (nonatomic, retain) GameScreenViewController *gameScreenViewController;
#end
//myAppAppDelegate.m
-(void) flipToGameScreen
{
GameScreenViewController *aGameScreenView = [[GameScreenViewController alloc] initWithNibName: #"GameScreen" bundle:nil];
[self setGameScreenViewController:aGameScreenView];
[aGameScreenView release];
[gameScreenViewController.view.frame = [[UIScreen mainScreen] applicationFrame];
[viewController.view removeFromSuperview];
[self.window addSubview:[gameScreenViewController view]];
}
Since your cell00 is to be set by a NIB it will be nil if you simply do [[GameScreen alloc] init]. It will only be set if the corresponding NIB is loaded (and a connection is actually set up).
If the cell can be accessed in your viewDidLoad, create a property on GameScreen and pass it through the property (or a dedicated initWithCell: or something).
If you have something like an IBOutlet GameScreen *aGameScreen; on your GameScreenViewController (and also established a connection to cell00 in the same NIB) you should access that instead.