I have a ContentPageViewController class, it has the IBOutlet stuff. I write my getter of ContentPageViewController in the ViewController like the following code.
ContentPageViewController.h
#interface ContentPageViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *busName;
#property (weak, nonatomic) IBOutlet UILabel *busTime;
#property (weak, nonatomic) IBOutlet UILabel *busType;
#end
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// instantiation from a storyboard
ContentPageViewController *page = [self.storyboard instantiateViewControllerWithIdentifier:#"ContentPageViewController"];
self.page = page;
// send url request
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"https://api.apb-shuttle.info/now" ]];
[self sendURLRequest:request];
// add the view of ContendPageViewController into ViewController
[self.view addSubview:self.page.view];
}
// It works if i remove the following code
- (ContentPageVC *)page
{
if (_page) _page = [[ContentPageViewController alloc] init];
return _page;
}
Nothing happened when I updated it. And it gave me a nil.
- (void)updateUI
{
// I got null here
NSLog("%#", self.page.busName)
// The spacing style font
NSDictionary *titleAttributes = #{
NSKernAttributeName: #10.0f
};
NSDictionary *attributes = #{
NSKernAttributeName: #5.0f
};
self.page.busName.attributedText = [[NSMutableAttributedString alloc] initWithString:bus.name
attributes:titleAttributes];
self.page.busTime.attributedText = [[NSMutableAttributedString alloc] initWithString:bus.depart
attributes:titleAttributes];
self.page.busType.attributedText = [[NSMutableAttributedString alloc] initWithString:bus.note
attributes:attributes];
}
The following code is when I called the updateUI:
- (void)sendURLRequest:(NSURLRequest *)requestObj
{
isLoading = YES;
[RequestHandler PerformRequestHandler:requestObj withCompletionHandler:^(NSDictionary *data, NSError *error) {
if (!error) {
bus = [JSONParser JSON2Bus:data];
// Add the bus object into the array.
[self.busArray addObject: bus];
[[NSOperationQueue mainQueue] addOperationWithBlock: ^{
[self updateUI];
isLoading = NO;
}];
} else {
NSLog(#"%#", [error localizedDescription]);
}
}];
}
But it worked if I removed the getter above.
I have no idea how it works, please give me some hint. Thanks.
Check your IBOutlet is connected.
Check the method you are calling isn't called before the view is created from the storyboard/nib
EDIT
The lines of code that you added, are overriding your getter. And every time you call self.page, your creating a new instance!
// It works if i remove the following code
- (ContentPageVC *)page
{
if (_page) _page = [[ContentPageViewController alloc] init];
return _page;
}
It should be like so:
// It works if i remove the following code
- (ContentPageVC *)page
{
if (!_page) _page = [[ContentPageViewController alloc] init]; // Added the ! mark, only if nil you would create a new instance.
return _page;
}
Plus you are calling alloc init on it, so Its not the same instance from storyboard!
So you should do this:
- (ContentPageVC *)page
{
if (!_page) _page = [self.storyboard instantiateViewControllerWithIdentifier:#"ContentPageViewController"];
return _page;
}
And remove this lines of code:
// instantiation from a storyboard
ContentPageViewController *page = [self.storyboard instantiateViewControllerWithIdentifier:#"ContentPageViewController"];
self.page = page;
Every time you call "self.page" the override getter function will call. and return the same instance.
Related
I run this code inside my viewDidLoad method to fetch data from Firebase to put it in a UIPageViewController
#interface MapViewController () <RoutesPageDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (weak, nonatomic) RoutesPageViewController *routesPageViewController;
#property (weak, nonatomic) FIRFirestore *db;
#end
#implementation MapViewController
- (void) viewDidLoad {
[super viewDidLoad];
self.db = [FIRFirestore firestore];
for (UIViewController *obj in self.childViewControllers) {
if ([obj isKindOfClass:[RoutesPageViewController class]]) {
self.routesPageViewController = (RoutesPageViewController *)obj;
self.routesPageViewController.routesPageDelegate = self;
}
}
FIRCollectionReference *routesRef = [self.db collectionWithPath:#"routes"];
[routesRef getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error != nil) {
// TODO: handle error
} else {
NSMutableArray<RouteModel*> *routes = [NSMutableArray array];
// For each route
for (FIRDocumentSnapshot *document in snapshot.documents) {
RouteModel *route = [[RouteModel alloc] init];
route.title = document.data[#"title"];
route.color = document.data[#"color"];
route.city = document.data[#"city"];
[routes addObject:route];
}
[self.routesPageViewController setRoutes:routes];
}
}];
}
And this is the called setRoutes method:
- (void) setRoutes:(NSMutableArray<RouteModel *> *)routes {
self.routes = routes;
NSMutableArray<RoutePageViewController *> * routeViewControllers = [NSMutableArray array];
for (RouteModel * route in routes) {
[routeViewControllers addObject:[self viewControllerAtIndex:[routes indexOfObject:route]]];
}
[self setViewControllers:routeViewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}
When the setRoutes method gets executed it throws the error in the image below, saying that it cannot dereference it:
The setRoutes methods gets executed inside a block.
And I get this weird thread stack:
How can I solve this?
Your problem is here:
- (void) setRoutes:(NSMutableArray<RouteModel *> *)routes {
self.routes = routes;
invoking self.routes implicity calles the setter setRoutes which causes a recursive infinite calls as indicated by your stack.
At the time the block is passed onto getDocumentsWithCompletion method is executed, routes array has already been deallocated and set to nil because no one is retaining it anywhere outside the block.
You should either move it into the block or declare it as a class property so it won't be thrown out of memory while class instance is alive.
[routesRef getDocumentsWithCompletion:^(FIRQuerySnapshot *snapshot, NSError *error) {
NSMutableArray<RouteModel*> *routes = [NSMutableArray array];
...
[self.routesPageViewController setRoutes:routes];
}];
After the update:
Doing self.routes = routes invokes setRoutes: which in turn is causing a loop in your code. You should change it to:
- (void)setRoutes:(NSMutableArray<RouteModel *> *)routes {
if (_routes != routes) {
_routes = routes;
...
}
}
I am trying to include MWPhotoBrowser in my project
When its used as given in the sample it working fine.
But when a new viewcontroller is subclassed from MWPhotoBrowser, photos are not loaded except empty black theme.
Delegate methods are not getting called. As the controller is subclass of MWPhotoBrowser, I assume there is no need to set it explicitly.
Storyboard is used and the nib class in it is set.
.h file
#interface MDRPhotoViewerController : MWPhotoBrowser
{
NSMutableArray *_selections;
}
#property (nonatomic, strong) NSMutableArray *photos;
#property (nonatomic, strong) NSMutableArray *thumbs;
#property (nonatomic, strong) NSMutableArray *assets;
#property (nonatomic, strong) NSMutableIndexSet *optionIndices;
#property (nonatomic, strong) UITableView *tableView;
#property (nonatomic, strong) ALAssetsLibrary *ALAssetsLibrary;
- (void)loadAssets;
#end
**.m file **
- (void)viewWillAppear:(BOOL)animated
{
NSMutableArray *photos = [[NSMutableArray alloc] init];
NSMutableArray *thumbs = [[NSMutableArray alloc] init];
//mwphotobrowser options setup
BOOL displayActionButton = YES;
BOOL displaySelectionButtons = NO;
BOOL displayNavArrows = NO;
BOOL enableGrid = YES;
BOOL startOnGrid = NO;
BOOL autoPlayOnAppear = NO;
//loading data
NSArray *photosDataArray = [MDRDataController GetPhotos]; //creating array
for (NSString *urlString in photosDataArray) { //Formating the data source for images
NSString *urlFullString = [NSString stringWithFormat:#"%#%#",KBASEURL,urlString];
//Photos
[photos addObject:[MWPhoto photoWithURL:[NSURL URLWithString:urlFullString]]];
//thumbs
[thumbs addObject:[MWPhoto photoWithURL:[NSURL URLWithString:urlFullString]]];
}
// Options
self.photos = photos;
self.thumbs = thumbs;
// Create browser
self.displayActionButton = displayActionButton;
self.displayNavArrows = displayNavArrows;
self.displaySelectionButtons = displaySelectionButtons;
self.alwaysShowControls = displaySelectionButtons;
self.zoomPhotosToFill = YES;
self.enableGrid = enableGrid;
self.startOnGrid = startOnGrid;
self.enableSwipeToDismiss = NO;
self.autoPlayOnAppear = autoPlayOnAppear;
[self setCurrentPhotoIndex:0];
// Test custom selection images
// browser.customImageSelectedIconName = #"ImageSelected.png";
// browser.customImageSelectedSmallIconName = #"ImageSelectedSmall.png";
// Reset selections
if (displaySelectionButtons) {
_selections = [NSMutableArray new];
for (int i = 0; i < photos.count; i++) {
[_selections addObject:[NSNumber numberWithBool:NO]];
}
}
self.title = #"Phots";
//[self reloadData];
}
Debugging performed
Considering the image template of mwphotobrowser, tried reloading the code.
Shifted the code between viewwillappear and viewdidload.
Doesn't MWPhotoBrowser support this way or am i doing it wrong ?
For those who stumble upon this later...
If you look at MWPhotoBrowser.m you'll see various initializers:
- (id)init {
if ((self = [super init])) {
[self _initialisation];
}
return self;
}
- (id)initWithDelegate:(id <MWPhotoBrowserDelegate>)delegate {
if ((self = [self init])) {
_delegate = delegate;
}
return self;
}
- (id)initWithPhotos:(NSArray *)photosArray {
if ((self = [self init])) {
_fixedPhotosArray = photosArray;
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super initWithCoder:decoder])) {
[self _initialisation];
}
return self;
}
The problem is there's no awakeFromNib initializer. Simplest solution is to fork the project and create the awakeFromNib initializer.
I have a quiz.vc and i am passing an NSString from on vc to another and it passes null. (i am using a UITextView)
Quiz.h
#property (nonatomic,strong) IBOutlet UITextView *textField;
#property (nonatomic, retain) NSString *userText;
Quiz.m
- (IBAction)next:(id)sender {
// i have tried NSString *userText also and passing in userText to sfvc.string
self.userText = self.textField.text;
selectFriendsViewController *sfvc = [[selectFriendsViewController alloc] init];
sfvc.string = self.userText;
}
selectFriendsViewController.h
#property (nonatomic, strong) NSString *string;
selectFriendsViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#", _string);
}
its always logging null at runtime? i have tried so many ways and methods!
any ideas as to how i can pass a string and not null???
Thanks
Your error
selectFriendsViewController *sfvc = [[selectFriendsViewController alloc] init];
sfvc.string = self.userText;
This create a new instance of selectFriendsViewController,but you do not use it.It will be dealloced when the method is done.So,you got nothing.
If you fire a segue in the IBAction,use prepareForSegue to pass data.
Edit,
If you fire a segue when the button clicked.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.destinationViewController isKindOfClass:[selectFriendsViewController class]]) {
selectFriendsViewController * dvc = (selectFriendsViewController*)segue.destinationViewController;
dvc.string = self.textField.text;
}
}
If you don't want to show null,use the below code
#pragma mark - check string is empty or not
- (IBAction)next:(id)sender
{
self.userText = self.textField.text;
selectFriendsViewController *sfvc = [[selectFriendsViewController alloc] init];
sfvc.string = [self checkEmpty:self.userText];
}
- (void)checkEmpty:(NSString *)check
{
#try
{
if (check.length==0)
check = #" ";
if([check isEqual:[NSNull null]])
check = #" ";
}
#catch (NSException *exception)
{
check = #" ";
}
}
I think u can't navigate and push the value to next viewController
use this code if you using xib files.
- (IBAction)next:(id)sender {
selectFriendsViewController *sfvc = [[selectFriendsViewController alloc] init];
sfvc.string = self.textField.text;
[self.navigationController pushViewController:sfvc animated:YES];
}
hope that code helps you.
Have a question about blocks in objective-c.
For example I have a list of actions.
I'm initializing an array of blocks:
self.actions = #[
^() { [self showObject:self.object_1]; },
^() { [self showObject:self.object_2]; },
^() { [self showObject:self.object_3]; }
];
And calling them when some row is pressed:
- (void)pressedRowAtIndex:(NSInteger)index {
if (index < actions.count) {
void (^action)() = [actions objectAtIndex:index];
if (action != nil) {
action();
}
}
}
And all works fine without problem. But when I init my actions array by using initWithObjects method:
self.actions = [NSArray alloc] initWithObjects:
^() { [self showObject:self.object_1]; },
^() { [self showObject:self.object_2]; },
^() { [self showObject:self.object_3]; },
nil
];
Than I get crash trying to get action by index by using objectAtIndex method of NSArray class.
I understand the difference between this inits. First one don't increase reference count like first do. But can someone explain why it crash?
Edit:
All that I've found. Maybe I'm nub and somewhere else is another useful information.
There is no crash info in terminal:
Code for Onik IV:
Small example:
#interface ViewController () {
NSArray *actions;
}
#property (nonatomic, strong) NSString *object1;
#property (nonatomic, strong) NSString *object2;
#property (nonatomic, strong) NSString *object3;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
actions = [[NSArray alloc] initWithObjects:
^() { [self showObject:self.object1];},
^() { [self showObject:self.object2]; },
^() {[self showObject:self.object3]; },
nil];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.object1 = #"object 1";
self.object2 = #"object 2";
self.object3 = #"object 3";
void(^firsSimpleBlock)(void) = [actions lastObject];
firsSimpleBlock();
void(^simpleBlock)(void) = [actions firstObject];
simpleBlock();
}
-(void)showObject:(NSString *)object
{
NSLog(#"Show: %#",object);
}
#end
Try something like this.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
(^someBlock)(void) = ^void(void){
self.object1;
};
actions = [[NSArray alloc] initWithObjects:
[someBlock copy],
[someOtherBlock copy],
[anotherBlock copy],
nil];
}
Blocks are allocated on the stack and are therefor removed when the frame is removed from the stack leading to sail pointers for all pointers pointing to that block. When you allocate a object with the literal "#" sign the object is allocated in a pool so all literals that are the "same" point to the same instance and are never deallocated.
NSString *a = #"A";
NSString *b = #"A";
points to the same instance of a string, while:
NSString *a = [NSString stringWithFormat:#"A"];
NSString *b = [NSString stringWithFormat:#"A"];
are two different objects.
So it works when you are creating a literal array but when you add the blocks dynamically they will be removed when its time to use them therefor the BAD_ACCESS. Solution is to send "copy" message to the block that will copy it to the heap and the block will not be released.
It´s the same, you must have another kind of problem (sintax?).
Try this:
#interface ViewController ()
#property (nonatomic, strong) NSString *object1;
#property (nonatomic, strong) NSString *object2;
#property (nonatomic, strong) NSString *object3;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.object1 = #"object 1";
self.object2 = #"object 2";
self.object3 = #"object 3";
NSArray *actions = #[^() { [self showObject:self.object1];},
^() { [self showObject:self.object2]; },
^() {[self showObject:self.object3]; }
];
NSArray *secondActions = [[NSArray alloc] initWithObjects:
^() { [self showObject:self.object1];},
^() { [self showObject:self.object2]; },
^() { [self showObject:self.object3];},
nil
];
void(^firsSimpleBlock)(void) = [actions lastObject];
firsSimpleBlock();
void(^simpleBlock)(void) = [secondActions firstObject];
simpleBlock();
}
-(void)showObject:(NSString *)object
{
NSLog(#"Show: %#",object);
}
#end
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
This is probably is pretty easy, but I'm stuck with it today.
The idea is that in my browser, I've create uiwebview and I want to implimate address bar in popover with it own class.
I can get the url from UItextfield from popover class to webview class, but when I get it uiwebview get lazy and it doesn't load it.
When I check it, debuger says that webview is null.
This is ViewController.h
#import <UIKit/UIKit.h>
#import "AdressBar.h"
#import "mypopoverController.h"
#interface ViewController : UIViewController<AddressbarDelegate>
{
UIWebView* mWebView;
mypopoverController *popoverController;
}
#property (nonatomic, retain) IBOutlet UIWebView* webPage;
#end
This is ViewController.m:
#import "mypopoverController.h"
#import "MyOwnPopover.h"
#import "ViewController.h"
#import "AdressBar.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize webPage = mWebView;
- (void)viewDidLoad
{
[super viewDidLoad];
addressBar = [[AdressBar alloc] init];
addressBar.delegate = self;
[edittext addTarget:self action:#selector(showPopoverAdressBar:forEvent:) forControlEvents:UIControlEventTouchUpInside];
NSURL *url = [NSURL URLWithString:#"http://www.google.lv"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[mWebView setScalesPageToFit:YES];
[mWebView setDelegate:self];
[mWebView loadRequest:request];
}
-(void)loadReceivedAddress:(NSURLRequest *)url{
NSLog(#"url= %#", url);//there url always is not null and mWebView should load it
if(mWebView != nil){
[mWebView loadRequest:url];
}else{
NSLog(#"mWebView is null");//...but there it say's that it's null
}}
-(void)showPopoverAdressBar:(id)sender forEvent:(UIEvent*)event
{
AdressBar *popoverControllesr = [[AdressBar alloc]init];
popoverControllesr.view.frame = CGRectMake(0,0, 600, 45);
popoverControllesr.view.backgroundColor = [UIColor whiteColor];
popoverController = [[mypopoverController alloc] initWithContentViewController:popoverControllesr];
popoverController.cornerRadius = 20;
if(_titles!=NULL){
popoverController.titleText = _titles;}else{
popoverController.titleText = #"Loading...";
}
popoverControllesr.address.text = absoluteString;
popoverController.popoverBaseColor = [UIColor orangeColor];
popoverController.popoverGradient= YES;
popoverController.arrowPosition = TSPopoverArrowPositionHorizontal;
[popoverController showPopoverWithTouch:event];
}
#end
This is AdressBar.h
#import <UIKit/UIKit.h>
#protocol AddressbarDelegate <NSObject>
#required
-(void)loadSomethingFromAddressBar:(NSURLRequest*)request;
#end
#interface AdressBar : UIViewController{
IBOutlet UIButton *cancel;
}
#property (nonatomic, retain) IBOutlet UITextField *address;
#property (nonatomic, retain) NSURLRequest *request;
#property(nonatomic, weak) id <AddressbarDelegate> delegate;
#end
This is AdressBar.m:
#import "AdressBar.h"
#import "ViewController.h"
#interface AdressBar ()
#end
#implementation AdressBar
#synthesize delegate = delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[_address setDelegate:self];
_address.clearButtonMode =
UITextFieldViewModeWhileEditing;
_address.keyboardType = UIKeyboardTypeURL;
[_address addTarget:self
action:#selector(loadAddresss)
forControlEvents:UIControlEventEditingDidEndOnExit];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)loadAddresss {
NSString* urlString = _address.text;
NSURL* url = [NSURL URLWithString:urlString];
if(!url.scheme)
{
NSString* modifiedURLString = [NSString stringWithFormat:#"http://%#", urlString];
url = [NSURL URLWithString:modifiedURLString];
}
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSLog(#"request= %#", request);
NSLog(#"text= %#", urlString);
if (request!=nil) {
if(delegate!=nil)
{NSLog(#"delegate not nil");
[delegate loadSomethingFromAddressBar:request];
}else{
NSLog(#"delegate is nil");//There delegate always is nil
}
}
}
#end
Make sure that your web view Outlet & Delegate is correctly conncted.
There no space between URL Address because I also face the same issue. Try to open that url in web-Browser.
If this is your view hierarchy
---- In ViewController
---- Showing AddressBar View in POPOver
---- Remove POPover and display ViewController's web view
I suggest create a custom delegate method in AddressBar and when you remove the popOver trigger the delegate method. Implement the delegate in your ViewContrller and call loadSomethingFromAddressBar in that implemented delegate method
Note : make sure you have connected you webpage IBOutlet to your nib file.
// In Adressbar.h
#protocol AddressbarDelegate <NSObject>
#required
-(void)loadYourWebViewNow:(NSURLRequest*)request;
#end
#interface Addressbar : UIViewController
{
}
#property(nonatomic, weak) id <AddressbarDelegate>
// In Adressbar.m
- (void)loadAddresss {
NSString* urlString = _address.text; //geting text from UItextField
NSURL* url = [NSURL URLWithString:urlString];
if(!url.scheme)
{
NSString* modifiedURLString = [NSString stringWithFormat:#"http://%#", urlString];
url = [NSURL URLWithString:modifiedURLString];
}
NSURLRequest *request = [NSURLRequest requestWithURL:url];
if (request!=nil) {
NSLog(#"request is good");
if(_delegate!=nil)
{
[_delegate loadYourWebViewNow:request];
}
}
// In your ViewController.h
#interface ViewController : UIViewController<AddressbarDelegate>
{
//AdressBar *addressBar;
}
// In your ViewController.m implement the delegate method and set the delegate
#implementation ViewController
-(void)viewDidLoad
{
Remove these two below lines on viewDidLoad
//addressBar = [[Adressbar alloc] init];
//addressbar.delegate = self;
}
-(void)loadYourWebViewNow:(NSURLRequest*)request
{
[self loadSomethingFromAddressBar:request];
}
-(void)showPopoverAdressBar:(id)sender forEvent:(UIEvent*)event
{
AdressBar *popoverControllesr = [[AdressBar alloc]init];
popoverControllesr.delegate = self; // set the delegate here to this object.
popoverControllesr.view.frame = CGRectMake(0,0, 600, 45);
popoverControllesr.view.backgroundColor = [UIColor whiteColor];
popoverController = [[mypopoverController alloc] initWithContentViewController:popoverControllesr];
popoverController.cornerRadius = 20;
if(_titles!=NULL){
popoverController.titleText = _titles;}else{
popoverController.titleText = #"Loading...";
}
popoverControllesr.address.text = absoluteString;
popoverController.popoverBaseColor = [UIColor orangeColor];
popoverController.popoverGradient= YES;
popoverController.arrowPosition = TSPopoverArrowPositionHorizontal;
[popoverController showPopoverWithTouch:event];
}