Visual Studio Xamarin.iOS Xcode Objective-C - WKWebView implementation - objective-c

I am currently trying to display a simple HTML-page within my iOS app used to control my smart mirror, which is supposed to display the current content displayed by the mirror. Unfortunately, I can't find any helpful tutorials online (only for the programming language Swift, which I am not allowed to use) and can't figure out how to properly implement WKWebView. If anyone could provide me with a useful tutorial or maybe even guide me through the steps, I would be very thankful!
EDIT: I've made some progress, however, the page won't load and will only display a white screen.
It is supposed to display a loading indicator (until the page is loaded), which is displayed for a fraction of a second before the screen turns white.
Here's my code:
"ThirdViewController.h":
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
#interface ThirdViewController : UIViewController <WKNavigationDelegate>
#property (strong, nonatomic) IBOutlet WKWebView *webView;
#property (strong, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
#end
"ThirdViewController.m":
#import "ThirdViewController.h"
#implementation ThirdViewController
#synthesize webView;
- (void) viewDidLoad {
[super viewDidLoad];
webView = [[WKWebView alloc] initWithFrame:[[self view] bounds]];
NSURL *url = [NSURL URLWithString:#"http://192.168.178.34:8080/"];
NSURLRequest *urlReq = [NSURLRequest requestWithURL:url];
[webView loadRequest:urlReq];
[self.view addSubview:webView];
}
- (void) webViewdidCommitNavigation:(WKNavigation *)navigation {
[self.activityIndicator startAnimating];
}
- (void) webViewdidFinishNavigation:(WKNavigation *)navigation {
[self.activityIndicator stopAnimating];
self.activityIndicator.hidesWhenStopped = YES;
}
#end
I have also added the following to Info.plist to allow for the use of non-https URLs:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Sincerely,
Lukas

If you want the same effect with the Objecive-C, you could have a try with following C# code:
public partial class ViewController : UIViewController
{
private readonly NSUrl url = new NSUrl("https://visualstudio.microsoft.com/xamarin/");
public UIActivityIndicatorView indicatorView;
protected ViewController(IntPtr handle) : base(handle) { }
partial void OpenWebView(UIButton sender)
{
indicatorView = new UIActivityIndicatorView(new CoreGraphics.CGRect(UIScreen.MainScreen.Bounds.Size.Width/2 - 25, UIScreen.MainScreen.Bounds.Size.Height/2 - 25, 25 ,25));
indicatorView.Hidden = true;
var webView = new WKWebView(View.Frame, new WKWebViewConfiguration());
View.AddSubview(webView);
View.AddSubview(indicatorView);
webView.NavigationDelegate = new CustomNavigationDelegate(this);
webView.LoadRequest(new NSUrlRequest(url));
}
partial void OpenSafari(UIButton sender)
{
UIApplication.SharedApplication.OpenUrl(url);
}
partial void OpenSafariViewController(UIButton sender)
{
var viewController = new SFSafariViewController(url);
PresentViewController(viewController, true, null);
}
}
internal class CustomNavigationDelegate : WKNavigationDelegate
{
private ViewController viewController;
public CustomNavigationDelegate(ViewController viewController)
{
this.viewController = viewController;
}
public override void DidCommitNavigation(WKWebView webView, WKNavigation navigation)
{
viewController.indicatorView.Hidden = true;
viewController.indicatorView.StartAnimating();
}
public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
viewController.indicatorView.StopAnimating();
viewController.indicatorView.HidesWhenStopped = true;
}
}

Related

Modifying string content in NSTextView works under viewDidLoad method, but not under myMethod

I am trying to update the contents of an NSTextView that is connected to myViewController as a referencing outlet to the Files Owner which is the subclass myViewController.
When I use an IBAction from a button, or use the viewDidLoad method of the controller, I can update the text fine. However, when I try run the method from another class (referred to in this example as anotherViewController), it runs the method, but the textview does not change.
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
self.outText.string = #"I am updated text! I also work!";
}
- (void)updateTextView:(NSString *)argText {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
- (void)updateTextViewWithoutArg {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
#end
In anotherViewController.m , which has all the relevant imports, I call this:
myViewController *viewtask = [[myViewController alloc] init];
[viewtask updateTextViewWithoutArg];
Nothing happens. The method runs and logs that it should have updated, but no text updates. I have tried many different approaches, including textstorage and scrollrange methods, they all work the already working sections, but make no difference in the sections not working.
I've also tried just for fun:
myViewController *viewtask;
[viewtask updateTextViewWithoutArg];
Also using the instance variable _outText
Also using [self.outText setString:#"string"];
Also using [_outText setString:#"string"];
Again, they work but only in the already working sections.
This should be simple but isn't logical to me. In swift all I need to do is
self.outText.string = "I update whenever I'm called!"
Views you create in Interface Builder are lazily created, so if you access them before viewDidLoad is called they are nil.
If your case, calling
myViewController *viewtask = [[myViewController alloc] init];
does not cause the views to be created so when you call
[viewtask updateTextViewWithoutArg];
self.outText is nil.
You can see that this is what is happening by updating your code as below:
- (void)updateTextView:(NSString *)argText {
NSAssert(self.outText != nil, #"self.outText must not be nil");
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
you should see the assert fire.
I appear to have found a solution by making myViewController a singleton class and using sharedInstance. For this particlar app, myViewController is a debug output window and will never need to be placed in another view.
I won't accept this answer yet, as it's not the best one I'm sure. There may still be a proper solution presented that allows finding the applicable myViewController instance, and modifying the outText property attached to it. Using this singleton makes subclassing tedious as I would have to make a new class for every instance if I wanted to be able to address say 10 View Controllers.
Anyway - the way I've been able to satisfy my simple requirement:
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
+ (id)sharedInstance;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
static myViewController *sharedInstance = nil;
+ (myViewController *)sharedInstance {
static myViewController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[myViewController alloc] init];
});
return sharedInstance;
}
- (void)viewDidLoad {
[super viewDidLoad];
sharedInstance = self;
}
- (void)viewDidUnload {
sharedInstance = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
sharedInstance.outText.string = #"Button Pressed";
}
- (void)updateTextView:(NSString *)argText {
sharedInstance.outText.string = argText;
}
- (void)updateTextViewWithoutArg {
sharedInstance.outText.string = #"I make it to the TextView now";
}
#end
Now when I use this code from within anotherViewController.m it updates the right instance:
[myViewController.sharedInstance updateTextView:#"Updating with this string"];

Moving UIImagePickerController into a separate class

I've been trying to solve the same exact same problem as this question for a while, but am having trouble doing so. I've only been on XCode a few months, so help would be greatly appreciated.
In my parentVC.m I have this:
parentVC.m
- (IBAction)goToCamera:(id)sender {
[_camera takePhoto];
} return;
}
This calls the takePhoto method in an equivalent of James's PhotoManager class: an NSObject Class called Camera - where I setup the UIImagePickerController. There I state the method in the header:
Camera.h
- (void) takePhoto;
then in the implementation:
Camera.m
#class ParentVC;
#interface Camera () <UIImagePickerControllerDelgate, UINavigationControllerDelegate>
#property (nonatomic, retain) ParentVC *parentVC;
#end
#implementation Camera
-(void)takePhoto
{
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.delegate = self;
[_parentVC presentViewController:cameraUI animated:NO completion:nil];
}
else{
DLog(#"camera wouldn't load");
}
}
#end
and no luck. I've tried many iterations and can't figure it out. Any ideas?

Launch image not working correctly no matter what

I'm trying to set a launch image to my application, however, it will not work the right way no matter what I do.
My app is landscape only(left or right), but I though I would just put a portrait launch image anyways.
The launch image only shows up if I check the "portrait" box and uncheck the "Landscape left" and "Landscape right" boxes in the "Deployment Info" settings. Obviously I can't do that because it will mess up my whole app.
I tried changing shouldAutorotateToInterfaceOrientation: to return YES in all of my view controllers, but that didn't work.
Any help is appreciated. Thanks!
-Xerif
I was having the same exact probably as your having and unfortunately i have found out that this is a glitch/bug in the Xcode software. I have come up with code that can display a launch image in landscape mode without any size requirements! Try to keep up with me and message me if you have any problems. Here are the steps:
Create a new objective-c file with a subclass of NSObject. Name it GameData.
inside GameData.h enter this code:
#import <Foundation/Foundation.h>
#interface GameData : NSObject
#property (assign, nonatomic) int Mainint;
+(instancetype)sharedGameData;
#end
Now inside of GameData.m enter this code:
#import "GameData.h"
#implementation GameData
+ (instancetype)sharedGameData
{
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(void)launchImage
{
[GameData sharedGameData].Mainint = [[NSUserDefaults standardUserDefaults] integerForKey:#"mainint"];
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:#"mainint"];
}
Now go into your AppDelegate.h file and enter this:
#import "GameData.h"
Now go into your AppDelegate.m file and enter this in the below method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:#"mainint"]; //Add This
// Override point for customization after application launch.
return YES;
}
Now go into your storyboard and drag a UILabel onto your initial View Controller or the View controller that shows first when app is launched. Don't worry we will hide this label so you will not see it.
Now drag a UIImageView across the entire screen of your initial View Controller
Now go into your initial View Controllers .h file (mine is called ViewController.h) and add the following:
#import <UIKit/UIKit.h>
#import "GameData.h" //Add this
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIImageView *launchImage; //Add this
#property IBOutlet UILabel *seconds; //Add this
#property NSTimer *timer; //Add this
#end
Now go into your initial view controller .m file (mine is ViewController.m) and add the following:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize launchImage;
#synthesize timer;
#synthesize seconds;
- (void)viewDidLoad
{
[GameData sharedGameData].Mainint = [[NSUserDefaults standardUserDefaults] integerForKey:#"mainint"];
seconds.hidden = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countUp) userInfo:nil repeats:YES];
if ([GameData sharedGameData].Mainint > 3) {
launchImage.hidden = YES;
}
[self countUp];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)countUp
{
[GameData sharedGameData].Mainint += 1;
seconds.text = [NSString stringWithFormat:#"%i", [GameData sharedGameData].Mainint];
if ([GameData sharedGameData].Mainint == 3) {
launchImage.hidden = YES;
[GameData sharedGameData].Mainint = 4;
[[NSUserDefaults standardUserDefaults] setInteger:[GameData sharedGameData].Mainint forKey:#"mainint"];
}
}
If you follow those steps exactly it should work and you should have no problems. If something does not work just let me know i'd be happy to help. I searched for days and days trying to find the answer and then finally figured it out through testing this code. Good Luck!

how do I push a view controller from a ViewCell in a UICollectionView

I'm trying to use a UICollectionView and it's viewCell. I have that going fine. But on the viewcell form I have a button that I intend to have go to a settings view. I'm trying to "push it onto the stack". Xcode complains of "no visible interface for the selector". Some code and the error message are below.
// TryColViewCell.h
#import <UIKit/UIKit.h>
#import "TryColViewContViewController.h"
#interface TryColViewCell : UICollectionViewCell
#property (weak, nonatomic) IBOutlet UILabel *outletNameLBL;
#property (weak, nonatomic) IBOutlet UILabel *outletCellLabel;
#property (readwrite) NSInteger intTest;
#property (readwrite) TryColViewContViewController *theHost;
- (IBAction)actionPlus1:(id)sender;
- (IBAction)actionMinus1:(id)sender;
- (IBAction)actionNameEdit:(id)sender;
// TryColViewCell.m
#import "TryColViewCell.h"
#import "SettingsViewController.h"
#implementation TryColViewCell {
#public int intCellNumber; //TODO: get this to track the row/cell #
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
- (IBAction)actionPlus1:(id)sender {
}
- (IBAction)actionMinus1:(id)sender {
}
- (IBAction)actionNameEdit:(id)sender {
NSLog(#"Name Edit");
SettingsViewController *viewControllerB =
[[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
viewControllerB.propName = _outletNameLBL.text;
[self pushViewController:viewControllerB animated:YES];
[self.theHost pushViewController:viewControllerB animated:YES];
}
#end
// Error message on PushVIewController line
TryColViewCell.m:83:11: No visible #interface for 'TryColViewCell'
declares the selector 'pushViewController:animated:'
Push the new view controller from the view controller that contains the collection view, not the cell. Only a subclass of UIViewController has the method pushViewController:animated. to override.
In your table view controller, do something like this, assuming you are using storyboard:
-(UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString* identifier = #"Cell";
CellSubClass *cell =
(CellSubClass*) [tableView dequeueReusableCellWithIdentifier:identifier];
[cell.upButton addTarget:self selector:#selector(actionPlus1:)
forControlEvent:UIControlEventTouchUpInside];
[cell.downButton addTarget:self selector:#selector(actionMinus1:)
forControlEvent:UIControlEventTouchUpInside];
...
}

How to create a global reference for iAd and implement in multiple Viewcontrollers

I'm having 5 ViewControllers in each of that i have to display iAd,so that i have to implement iAd code in each ViewControllers. Instead of that if i create set of common code in AppDelegate means i can just call that code wherever i need iAd to be displayed.
If anyone has implemented this iAd concept means help me to get out of this issue. Thanks in Advance.
Just create a Pointer to iAD in APP delegate.
.h:
#property (strong, nonatomic) ADBannerView *UIiAD;
.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UIiAD = [[ADBannerView alloc] init];
return YES;
}
Then in your ViewControllers do this:
.h:
#property (strong, nonatomic) ADBannerView *UIiAD;
.m:
- (AppDelegate *) appdelegate {
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (void) viewWillAppear:(BOOL)animated {
UIiAD = [[self appdelegate] UIiAD];
UIiAD.delegate=self;
// CODE to correct the layout
}
- (void) viewWillDisappear:(BOOL)animated{
UIiAD.delegate=nil;
UIiAD=nil;
[UIiAD removeFromSuperview];
}
Do this for all the view controllers with appropriate code to redesign the layout!
hi this looks like a good answer but don't you have to import the AppDelegate in your m files
#import "AppDelegate.h"
and shouldn't you hide the add if no network connection or add not showing
In the AppDelegate.h:
#import <iAd/iAd.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
ADBannerView *_bannerView;
...
...
}
#property (nonatomic, retain) ADBannerView *_bannerView;
In the AppDelegate.m:
#synthesize _bannerView;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
if ([ADBannerView instancesRespondToSelector:#selector(initWithAdType:)]) {
self._bannerView = [[ADBannerView alloc] initWithAdType:ADAdTypeBanner];
} else {
self._bannerView = [[ADBannerView alloc] init];
}
...
}
In the view controllers that you need to add iAd, create a container UIView with name 'vwAd' and make the connection in xib file where you want to display iAds.
#interface ViewController : UIViewController<ADBannerViewDelegate>
{
IBOutlet UIView *vwAd;
...
...
}
- (void)layoutAnimated:(BOOL)animated
{
CGRect contentFrame = self.view.bounds;
if (contentFrame.size.width < contentFrame.size.height) {
self.appDelegate._bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
} else {
self.appDelegate._bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
}
CGRect bannerFrame = self.appDelegate._bannerView.frame;
if (self.appDelegate._bannerView.bannerLoaded) {
bannerFrame.origin.y = 0;
} else {
bannerFrame.origin.y = vwAd.frame.size.height;
}
[UIView animateWithDuration:animated ? 0.25 : 0.0 animations:^{
self.appDelegate._bannerView.frame = bannerFrame;
}];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
...
[self.appDelegate._bannerView removeFromSuperview];
self.appDelegate._bannerView.delegate = nil;
self.appDelegate._bannerView.delegate = self;
[vwAd addSubview:self.appDelegate._bannerView];
[self layoutAnimated:NO];
}
Please also review iAdSuite examples from Apple. Original layoutAnimated function can be found in iAdSuite examples. Do not forget to add the delegate functions from iAdSuite example into your view controller.