weakSelf when setting frame - ios

I recently read this post and figured it would be a good idea to use the tips from the article. I'm using it in blocks, but should I also use it in the 'block' below. Is the 'block' below a real block?
avatar.frame = ({
CGRect frame = avatar.frame;
frame.origin.y = CGRectGetMaxY(self.view);
frame;
});
That would become:
__weak typeof(self)weakSelf = self;
avatar.frame = ({
__strong typeof(weakSelf)strongSelf = weakSelf;
CGRect frame = avatar.frame;
frame.origin.y = CGRectGetMaxY(strongSelf.view);
frame;
});

It's not a block. It is an GCC C extension, which causes a code block
to return a value if enclosed within brackets and parentheses.
This not only segregates configuration details into initialization,
but the additional scope allows generic variable names like frame,
button, and view to be reused in subsequent initializations. No more
loginButtonFrame = ... / signupButtonFrame = ...!
source: http://nshipster.com/new-years-2014/

This is not a block, block start with ^.
I believe this code:
({
CGRect frame = avatar.frame;
frame.origin.y = CGRectGetMaxY(self.view);
frame;
});
Is a way to create a CGRect.

Related

create a UILabel when view appears and destroy it when view disappears

For the tutorial for my app, I am trying to create a UILabel that drifts across the displayed screen when a view appears and then is destroyed so that if the user comes back to that view during the tutorial, the UILabel will be created anew and again drift across the page. Here's a sample of one of the custom view controllers I am displaying with my UIPageViewController:
//this is just a custom UILabel with some padding
#property (nonatomic) PaddedLabel *directionsLabel;
//I have tried setting UILabel to nil or removing it from view
-(void)viewWillDisappear:(BOOL)animated
{
NSLog(#"view is disappearing");
//this does not remove the label
self.directionsLabel = nil;
//nor does this
[self.directionsLabel removeFromSuperview];
}
- (void)viewDidAppear:(BOOL)animated
{
[self messageTutorial];
}
- (void)messageTutorial
{
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
CGFloat height = [[UIScreen mainScreen] bounds].size.height;
PaddedLabel *directionsLabel = [[PaddedLabel alloc] initWithFrame:CGRectMake(_width/2, _height/2, 100, 100)];
directionsLabel.text = #"test";
CGRect f = directionsLabel.frame;
f.size = size;
directionsLabel.frame = f;
directionsLabel.center = self.view.center;
f = directionsLabel.frame;
f = directionsLabel.frame;
f.origin.y = .1*height;
directionsLabel.frame = f;
[self.view addSubview:directionsLabel];
[UIView animateWithDuration:TUTORIAL_DISAPPEAR_TIME animations:^{
directionsLabel.alpha = .5;
CGRect f = directionsLabel.frame;
f.origin.y = height - f.size.height*1.4;
directionsLabel.frame = f;
NSLog(#"animating");
} completion:^(BOOL finished) {
[directionsLabel removeFromSuperview];
//this also doesn't actually remove the label
}];
}
The problem is that if the user pages back to see this view she now sees a new label and the old one, so that if you page back and forth back and forth you end up with many many labels all saying the same thing, in different stages of progressing across the screen.
How can I remove the UILabel when the view disappears and add/create a new one when the view appears/reappears?
Thank you.
The code in your viewWillDisappear method is backwards. You need:
- (void)viewWillDisappear:(BOOL)animated {
NSLog(#"view is disappearing");
[self.directionsLabel removeFromSuperview];
self.directionsLabel = nil;
}
As you had it, setting self.directionsLabel to nil before trying to remove it results in a no-op.
Also, be sure to set self.directionsLabel when you create the label.
Instead of setting your label to nil and effectively destroying the label object (assuming automatic reference counting is on) rather use the following method to hide and show the label as and when your need it.
[self.directionsLabel setHidden:YES]; // hides it
[self.directionsLabel setHidden:NO]; // shows it
You've got the right idea setting objects you're not using to nil and removing them from the super view but it's over kill. A UILabel object uses a negligible amount of memory and you're better off creating the object once and then changing it's properties as you need to.
You don't seem to be setting self.directionsLabel to anything when you create the directionsLabel inside the messageTutorial method. It is a local instance of the label there. You should set it in the method somewhere.
Afterwards, removing it from the superview in viewWillDisappear will work (tested to verify).

UIWebView is not resizing in iOS8

I am working on table view based iOS application for iOS 7 and iOS8 using XIB, where I need to display UIWebView as footer view of table view. I have added UIWebView in my XIB, I don't know why I can't able to scroll full UIWebView according to the contents, it seems like locked.
Here is my code:
- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
webViewSocial.scrollView.scrollEnabled = NO;
CGFloat height = [[webViewSocial stringByEvaluatingJavaScriptFromString:#"document.height"] floatValue];
CGFloat width = [[webViewSocial stringByEvaluatingJavaScriptFromString:#"document.width"] floatValue];
CGRect frame = webViewSocial.frame;
frame.size.height = height;
frame.size.width = width;
webViewSocial.frame = frame;
tblAlerts.tableFooterView = webViewSocial;
}
The parameter of your webView delegate method is "aWebView" but you're using "webViewSocial" in the method body. If you intend to apply the implementation to whichever webView called it, you'll need to match the local variable to the name of the parameter. That is unless "webViewSocial" is an ivar (in which case I'd use the _ prefix just to avoid confusion. If it's a property of your class I'd use self.webViewSocial.
This is Swift 3 version
func webViewDidFinishLoad(_ webView: UIWebView) {
var frame = yourWebView.frame
frame.size.height = 1
let fittingSize = yourWebView.sizeThatFits(CGSize.zero)
frame.size = fittingSize
heightOfyourWebView.constant = fittingSize.height
tblAlerts.tableFooterView = yourWebView;
}

Creating object programmatically and changing that object's position

I am trying to create a couple of UIImageViews (preferably with a for loop) and then afterwards move them, but I don't know how because I'm having trouble figuring out how to reference them if they will be made programmatically.
So I want something like this:
for (int i = 0; i < 2; i++)
{
UIImageView *iv = [[UIImageView alloc]initWithImage:[UIImage imageNamed: picturo]];
CGRect rect = CGRectMake((i * 50) + 50,30, 25, 25);
[iv setFrame:rect];
iv.tag = i;
[self.view addSubview:iv];
}
(iv with tag of 0).center.y = 80; // <-- How do I do that!!
Obviously this example has no practical use and I could just change y value of 'rect' above to 80, but I want to know how to:
a. Create multiple uiimageviews (or any object for that matter) and be able to identify/reference/manipulate each one individually (I don't know how but I would assume by either naming them uniquely or using tags) and
b. Be able to identify/reference/manipulate an object that was created programmatically.
creating views with tags works the way you have it.
To access those views use the viewWithTag property on a view to identify one of it's subviews:
UIView* subview = [self.view viewWithTag:0];
subview.center = CGPointMake(subview.center.x, 80);
(you cannot write to a view.center's x or y, you have to recreate the whole thing. Likewise with other geometric structs such as CGSize, CGRect)

UIView: layoutSubviews vs initWithFrame

When subclassing UIView, I usually place all my initialisation and layout code in its init method. But I'm told that the layout code should be done by overriding layoutSuviews. There's a post on SO that explains when each method gets called, but I'd like to know how to use them in practice.
I currently put all my code in the init method, like this:
MyLongView.m
- (id)initWithHorizontalPlates:(int)theNumberOfPlates
{
self = [super initWithFrame:CGRectMake(0, 0, 768, 1024)];
if (self) {
// Initialization code
_numberOfPlates = theNumberOfPlates;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.frame];
[scrollView setContentSize:CGSizeMake(self.bounds.size.width* _numberOfPlates, self.bounds.size.height)];
[self addSubview:scrollView];
for(int i = 0; i < _numberOfPlates; i++){
UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:#"a1greatnorth_normal_%d.jpg", i+1]];
UIImageView *plateImage = [[UIImageView alloc] initWithImage:img];
[scrollView addSubview:plateImage];
plateImage.center = CGPointMake((plateImage.bounds.size.width/2) + plateImage.bounds.size.width*i, plateImage.bounds.size.height/2);
}
}
return self;
}
It's the usual tasks: setting up the view's frame, initialising an ivar, setting up a scrollview, initialising UIImages, placing them in UIImageViews, laying them out.
My question is: which of these should be done in init, and which of these should be done in layoutSubviews?
Your init should create all the objects, with the required data. Any frame you pass to them in init should ideally be their starting positions.
Then, within layoutSubviews:, you change the frames of all your elements to place them where they should go. No alloc'ing or init'ing should take place in layoutSubviews:, only the changing of their positions, sizes etc...
In case you're autoresizing works perfectly with just autoresizingFlags, or autolayout, you may just use init to setup the whole view.
But in general your should do layouting in layoutSubviews, since this will be called on every change of the views frame and in other situation, where layout is needed again. Sometimes you just don't know the final frame of a view within init, so you need to be flexible as mentioned, or use layoutSubviews, since you do the layout there after the final size has been set.
As mentioned by WDUK, all initialization code / object creation should be in your init method or anywhere, but not in layoutSubviews.

MonoTouch: How to change control's location at runtime?

I want to change the location of an UIImageView at runtime. In IB, I can change X coordinate to relocate it. So, I tried this code:
imageView.Frame.X = 25;
But it doesn't have any effect. Why? I have the same problem for other controls as well. How to make it work?
The Frame property is a value type, this means that if you do:
imageView.Frame.X = 25
what you are actually doing is:
var temp = imageView.Frame;
temp.X = 25;
The value never reaches the imageView. With value types, you have to assign a fully constructed type, so for example:
var current = imageView.Frame;
current.X = 25;
imageView.Frame = current;
The Frame property is a value type, this means that if you do:
imageView.Frame.X = 25
what you are actually doing is:
var temp = imageView.Frame;
temp.X = 25;
The value never reaches the imageView. With value types, you have to assign a fully constructed type, so for example:
var current = imageView.Frame;
current.X = 25;
imageView.Frame = current;
// The Code is quite simple and self explanatory
dispatch_async(dispatch_get_main_queue(), ^{
self.myimageView.frame = CGRectMake(140, 70, 140, 128);
});
By using >dispatch_async(dispatch_get_main_queue(), ^{ I made the code to run in the main queue, not in the background.

Resources