Strange Table View Header issue when using Three20 TTTableViewController - ios

I am building a TTTableViewController subclass based on Three20 TTCatalog sample. In there they have defined a subclass of TTTableViewController which has search functionality, called SearchTextController, and it has a search bar appear in the table view header.
Here's what their loadView method looks like:
- (void)loadView {
[super loadView];
TTTableViewController* searchController = [[[TTTableViewController alloc] init] autorelease];
searchController.dataSource = [[[MockSearchDataSource alloc] initWithDuration:1.5] autorelease];
self.searchViewController = searchController;
self.tableView.tableHeaderView = _searchController.searchBar;
}
I tried to re implement the same kind of functionality in my own subclass of TTTableView. The problem is that I cannot get the search bar to appear.
Running these in loadView of TTCatalog SearchTestController (after the last line)
NSLog(#"_searchController.searchBar address %#",_searchController.searchBar.description);
NSLog(#"self.tableView.tableHeaderView address %#",self.tableView.tableHeaderView.description);
yields the same memory address for the layer (meaning the view got set correctly)
2012-07-03 12:00:02.326 TTCatalog[53493:f803] _searchController.searchBar address <UISearchBar: 0x6a5c940; frame = (0 0; 320 44); layer = <CALayer: 0x6a5ca40>>
2012-07-03 12:00:02.327 TTCatalog[53493:f803] self.tableView.tableHeaderView address <UISearchBar: 0x6a5c940; frame = (0 0; 320 44); layer = <CALayer: 0x6a5ca40>>
When I run the same NSLogs in my app, I get this:
2012-07-03 11:41:54.900 myApp[51991:f803] _searchController.searchBar address <UISearchBar: 0x6a43c20; frame = (0 0; 320 44); layer = <CALayer: 0x6a43ce0>>
2012-07-03 11:41:54.901 myApp[51991:f803] self.tableView.tableHeaderView address <UISearchBar: 0x6a43c20; frame = (0 0; 320 44); layer = <CALayer: 0x6a43ce0>>
The layers have the same memory address in my app as well, but the search bar is not visible in the table view header. Is there something else I am missing here?

Related

How to view the content UITableView's reusable pool

I was trying to deep dive into how the UITableView works internally. I've already read multiple posts regarding that like,
https://www.mikeash.com/pyblog/friday-qa-2013-02-22-lets-build-uitableview.html
I want to know if there is any way we can look into the content of the reusable pool, i.e. how many and what all UITableViewCell types are available inside it at a moment.
I already know that we'll have the cells as per the screen size and the cell height. But I'm trying to figure out what happens if the tableView accepts different types of cells at different indexPaths.
Well, you can dig down into the objects in debug...
I set a breakpoint in didSelectRowAt and looked into the Variables View pane:
I had one row (index path [0,3] that used TestCellB and 90 rows that used TestCell, and the table was scrolled to row 21 (so row [0,3] was well off-screen.
As you can see, there is an internal Dictionary named _reusableTableCells which, in my example, currently contains 1 TestCellB and 2 TestCells.
EDIT
I just gave this a try - might give you some additional info:
var reusableCells = tableView.value(forKey: "_reusableTableCells")
print(reusableCells)
Output:
Optional({
TestCellB = (
"<TranslateTest.TestCellB: 0x7f87b206aa00; baseClass = UITableViewCell; frame = (0 1612; 414 37); clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x600003a66380>>"
);
TestCellC = (
"<TranslateTest.TestCellC: 0x7f87b2894800; baseClass = UITableViewCell; frame = (0 205; 414 93); clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x600003a586e0>>",
"<TranslateTest.TestCellC: 0x7f87b3000e00; baseClass = UITableViewCell; frame = (0 298; 414 93); clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x600003a5fe40>>"
);
})

Memory not releasing when removing object - unclear on proper way to release in ARC

Xcode 5, iOS7
I have an app that creates UILabels in a loop, referencing them in an array.
After repeatedly calling the 'creation' method I'm noticing that my memory usage increases.
I'd like to clear the array and the related UILabel objects to free the memory,
as it appears it not happening with my current code. Instead, each time through my loops
I can see my memory usage increasing and never decreasing. I am currently using Storyboards and ARC.
How can I properly release the UILabels since it seems ARC is not doing it for me?
#implementation myViewController
CGPoint tmpPoint;
NSMutableArray *allDots;
/// more code here
//
// This method is called repeatedly from another loop
-(void)createLabels{
//remove any previous labels
//I'm pretty sure this is not actually clearing the UILabels but rather the pointer to the labels?
for(short i=0; i<10; i++){
UILabel *junkDot=[allDots objectAtIndex:i];
junkDot=nil;
}
// Either of these statements should clear the references/pointers
// [allDots removeAllObjects];
allDots=[[NSMutableArray alloc] init];
//create a new group of labels
for(short i=0; i<10; i++){
// code to generate Point values
[self makeLabel:tmpPoint];
}
}
-(UILabel*)makeLabel:(CGPoint)thePoint{
CGRect xFrame=CGRectMake(thePoint.x, thePoint.y, 40, 20);
UILabel *tmpLabel=[[UILabel alloc] initWithFrame:xFrame];
[self.view addSubview:tmpLabel];
[allDots addObject:tmpLabel];
return tmpLabel;
}
You need to read up on the basics of memory management.
In ARC, objects are kept in memory as long as there is at least one strong reference to them. As soon as there are no longer any strong references to an object it may be released.
An NSArray (or NSMutableArray) keeps a strong reference to any object that is added to it. If you remove an object from a mutable array (using, e.g. removeObjectAtIndex:) the array releases it's strong reference.
Your code that fetches objects from an array and then sets the local variable to nil does absolutely nothing but waste time:
for(short i=0; i<10; i++){
UILabel *junkDot=[allDots objectAtIndex:i];
junkDot=nil;
}
When you add a view object to your view hierarchy using addSubview:, the superview also keeps a strong reference to the object.
Thus, if you create a bunch of labels and both put them in an array and add them as subviews of your content view, each label has 2 strong references. A label won't be deallocated until it is both removed from it's superview AND removed from the array.
#implementation myViewController
CGPoint tmpPoint;
NSMutableArray *allDots;
Replace Above code with code
#implementation myViewController {
CGPoint tmpPoint;
NSMutableArray *allDots;
}
Unless you are keeping the UILabel objects on-screen in the view they should be gone from the array at least with nothing more than the removeAllObjects method. If they aren't, then something else is retaining them.
In your code you are pulling the items from an allDots array, but then you are using the UILabel still somewhere else. The array has released the object, but since it's still in use somewhere. Since the array only held references, you are just reducing the retain count by 1. The object isn't taking up any more or less memory, it just has one less retain on it.
You can easily set some NSLog or logging breakpoints along the way to see what's happening like this:
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *allDots = [#[] mutableCopy];
for(NSInteger i=0; i<10; i++){
UILabel *i = [UILabel new];
[allDots addObject: i];
}
NSLog(#"Contents of array %#", [allDots description]);
// Either of these statements should clear the references/pointers
[allDots removeAllObjects];
NSLog(#"Contents of array %#", [allDots description]);
// Do any additional setup after loading the view, typically from a nib.
}
2014-06-29 11:25:59.185 Loper[47174:2208182] Contents of array (
"<UILabel: 0x10b76c370; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b768ef0>>",
"<UILabel: 0x10b82b870; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b82b9d0>>",
"<UILabel: 0x10b834c10; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b82d510>>",
"<UILabel: 0x10b82a9b0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b8408b0>>",
"<UILabel: 0x10b82ab10; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b834dd0>>",
"<UILabel: 0x10b8326e0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b837a80>>",
"<UILabel: 0x10b832840; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b80ebb0>>",
"<UILabel: 0x10b8329a0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b841a90>>",
"<UILabel: 0x10b846290; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b80eeb0>>",
"<UILabel: 0x10b8465a0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b846700>>"
)
2014-06-29 11:25:59.185 Loper[47174:2208182] Contents of array (
)
To find out what's going on step by step, just throw a regular breakpoint in there and step through the code to see who is holding on to the references.
Other options are to just run the static analyzer and see if any retain loops are detected, or to open your project in Instruments with the leaks or allocation tools and see what's going on under the hood on device.

What does autoresize = RM+BM mean?

I get this when using the Xcode debugging console with this line:
po [[UIWindow keyWindow] recursiveDescription]
Then I get back lines with the recursive description like this
<UILabel: 0xb0b8170; frame = (138 106; 200 15); text = 'Distance'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0xb0b8220>>
What does "autoresize = RM+BM" and is there a place I can lookup the meaning of the letters? I also saw something like this "autoresize = LM+W+RM+TM+H+BM".
autoresize indicates which bits have been set in the autoresizingMask:
RM = UIAutoresizingFlexibleRightMargin
BM = UIAutoresizingFlexibleBottomMargin
The others follow a similar abbreviation syntax.
If you give all the constraints, then you can easily see all of them. These all indicates the UIAutoResizing.
RM means Right margin
BM means Bottom margin
W means Width
H means Height
LM means Left margin
TM means Top margin
<SIGMAPOINT_MPAdView: 0x10db888a0; frame = (0 0; 320 50); clipsToBounds = YES; autoresize = LM+RM; layer = <CALayer: 0x282b704c0>>

Why is an 'animations' property in UIImageView on custom UITableViewCell removed after calling [super prepareForReuse]?

recently, I am making a custom UITableViewCell with animatable UIImageView to play animated GIF. During making it, I found that an animation is not shown after the cell was reused.
It seems that UIImageView is affected by [self prepareForReuse]. When I did debugging it, I could get the following information.
** before calling [super prepareForReuse] **
(lldb) po self.testImageView
<UIImageView: 0x1ab681c0; frame = (39 0; 78 61); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; animations = { UIImageAnimation=<CAKeyframeAnimation: 0x1ab77870>; }; layer = <CALayer: 0x1ab68b60>> - (null)
** after calling [super prepareForReuse] **
(lldb) n
(lldb) po self.testImageView
<UIImageView: 0x1ab681c0; frame = (39 0; 78 61); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x1ab68b60>> - (null)
As it show, "animations = { UIImageAnimation=; };" is removed.
Fortunately, I could work around that problem by making a new instance of UIImage and setting it to UIImageView again as a workaround. For doing this workaround, it's important to re-create an instance of UIImage not to reuse an instance which is already set to UIImageView.
Anyway, what I really want to know is why prepareForReuse method removes the information about an animation.
Please let me know an internal reuse logic of the method which is hidden.
Upon calling [super prepareForReuse], stopAnimation is called on self.testImageView. I think this is the right logic being used so that the cell can be reused. You just need to call the [self.testImageView startAnimation] method once the cell is prepared for reuse. You need not resort to the "workaround" method that you have mentioned.
Found a solution. I had to set the UIImageView's image property to nil in prepareForReuse(), before setting a new (or even the same) animated UIImage.

How to display page scrubber with QLPreviewController in a subview

I am embedding a QLPreviewController in my own view controller by adding it as a child view controller like this (contentArea is a UIView property that determines which part of the screen the preview should use):
QLPreviewController* preview = [[QLPreviewController alloc] init];
preview.dataSource = self;
preview.delegate = self;
[self addChildViewController:preview];
CGFloat w = self.contentArea.frame.size.width;
CGFloat h = self.contentArea.frame.size.height;
preview.view.frame = CGRectMake(0, 0,w, h);
[self.contentArea addSubview:preview.view];
[preview didMoveToParentViewController:self];
self.qlPreviewController = preview;
This works so far. However, when I open a PDF, I do not get the page scrubber that is shown on the right side when the QLPreviewController is shown full screen (see screenshot).
Using DCIntrospect I see that there is a special subview of type QLScrubView responsible for this:
...
| <QLScrubView: 0x1274e990; frame = (963 64; 61 704); autoresize = LM+H; layer = <CALayer: 0x1274eb30>>
| | <QLThumbnailView: 0x11fc6460; frame = (17 329; 32 45); layer = <CALayer: 0x11fcb4b0>>
| | <QLThumbnailView: 0x11fd45a0; frame = (17 382; 32 45); layer = <CALayer: 0x11fbe830>>
| | <QLThumbnailView: 0x1274dae0; frame = (9 268; 48 61); layer = <CALayer: 0x1273b860>>
...
However, this scrub view is missing in the embedded use case.
Any ideas how to make this appear?
It displays automatically when you have more than one page. I would guess that there's something wrong with your PDF. Try other PDFs from other sources and see if you get the same problem. I use PDFs all the time and the scrubber shows up just fine.
My other thought is maybe resizing your preview controller is not behaving properly. Try loading the preview controller as a modal view controller (no parent) and see if the scrubber is still hidden.

Resources