Recently I updated my xcode project to work with iOS 7, but i faced a big problem. Because my whole application has only one background image (UIImageView added to key window) and all views are transparent, I face a problem when pushing UIViewController, because pushed view controller overlaps previous view (you can see it in the picture here: http://grab.by/qp0k). I can predict that this is because in iOS 7 push transition has been changed, because now it slides half a screen. Maybe anyone knows how to fix this issue?
This is how I set my key windows
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIImageView *background = [[UIImageView alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
background.image = [UIImage imageNamed:#"background.png"];
UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:self.viewController];
self.window.rootViewController = navi;
[self.window makeKeyAndVisible];
Afterwards when user clicks on "start workout" button I push my next view as always:
workoutView *w = [[workoutView alloc]initWithNibName:#"workoutView" bundle:nil];
[self.navigationController pushViewController:w animated:YES];
I did this.
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[self.view setAlpha:0];
}
Do not forget re set alpha when come back.
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.view setAlpha:1];
}
I solved the problem by implementing the new UINavigationControllerDelegate Method animationControllerForOperation.
For example:
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
PushTransition* transition = [PushTransition new];
[transition setNavigationControllerOperation: operation];
return transition;
}
PushTransition is a class that implements the UIViewControllerAnimatedTransitioning protocol and the two methods transitionDuration and animateTransition from that protocol. Additionally, i have added a property to pass the operation (tells me if it is a push or pop transition).
Just put the animation code for moving the views into the animateTransition as follows:
// the containerView is the superview during the animation process.
UIView *container = transitionContext.containerView;
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *fromView = fromVC.view;
UIView *toView = toVC.view;
CGFloat containerWidth = container.frame.size.width;
// Set the needed frames to animate.
CGRect toInitialFrame = [container frame];
CGRect fromDestinationFrame = fromView.frame;
if ([self navigationControllerOperation] == UINavigationControllerOperationPush)
{
toInitialFrame.origin.x = containerWidth;
toView.frame = toInitialFrame;
fromDestinationFrame.origin.x = -containerWidth;
}
else if ([self navigationControllerOperation] == UINavigationControllerOperationPop)
{
toInitialFrame.origin.x = -containerWidth;
toView.frame = toInitialFrame;
fromDestinationFrame.origin.x = containerWidth;
}
// Create a screenshot of the toView.
UIView *move = [toView snapshotViewAfterScreenUpdates:YES];
move.frame = toView.frame;
[container addSubview:move];
[UIView animateWithDuration:TRANSITION_DURATION delay:0
usingSpringWithDamping:1000 initialSpringVelocity:1
options:0 animations:^{
move.frame = container.frame;
fromView.frame = fromDestinationFrame;
}
completion:^(BOOL finished) {
if (![[container subviews] containsObject:toView])
{
[container addSubview:toView];
}
toView.frame = container.frame;
[fromView removeFromSuperview];
[move removeFromSuperview];
[transitionContext completeTransition: YES];
}];
described it and you can you are done. Additionally you can make any push or pop animation you like.
I fixed it by doing this when initialising the view:
self.view.clipsToBounds = YES;
You might want to look into a new iOS7 feature that allows you to define your own custom UIViewController transitions. Look in the docs for UIViewControllerTransitioningDelegate. Also, here's a link to an article about it: http://www.doubleencore.com/2013/09/ios-7-custom-transitions/
Ah, now I understand the issue. You were right, seems to be caused by the previous UIViewController not being hidden after the transition (because of the new transition effect).
There doesn't seem to be any SDK method to control this behavior. Short of redesigning the app to not requiring the background be static, you'll probably have to roll your own navigation. OSNavigationController is a complete reimplementation of UINavigationController that might help you out. If they haven't updated to the iOS 7 transition, you'll probably be good to go. If they have you can always use an older version.
I had the same problem. Try to load your background image in the init method. For me, it worked (sometimes):
For example:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.view.backgroundColor = [UIColor whiteColor];
[self.imageBack setImage:[UIImage imageNamed:#"mayBack.png"]];
}
return self;
}
However, you could see glimpses..
The best solution I found, beside implementing the new iOS7 transition protocol, is to implement a category, and use that category whenever you need it.
You can find the answer here
Setting the image to the background color solved the issue:
self.view.backgroundColor =
[UIColor colorWithPatternImage:[UIImage imageNamed:#"mainback.png"]];
Take a look at the UINavigationController category in that post (it solved me problem) :
https://stackoverflow.com/a/18882232/2826409
Props to #snoersnoer.
Here is the code in Swift 3.
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let pushTransition = SUPushTransition()
pushTransition.navigationControllerOperation = operation
return pushTransition
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// the containerView is the superview during the animation process.
let container = transitionContext.containerView
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
let toVC = transitionContext.viewController(forKey:UITransitionContextViewControllerKey.to);
if let from = fromVC,
let fromView = from.view,
let to = toVC,
let toView = to.view {
let containerWidth = container.frame.size.width
// Set the needed frames to animate.
var toInitialFrame = container.frame
var fromDestinationFrame = fromView.frame
if self.navigationControllerOperation == .push {
toInitialFrame.origin.x = containerWidth;
toView.frame = toInitialFrame;
fromDestinationFrame.origin.x = -containerWidth;
}
else if self.navigationControllerOperation == .pop {
toInitialFrame.origin.x = -containerWidth;
toView.frame = toInitialFrame;
fromDestinationFrame.origin.x = containerWidth;
}
// Create a screenshot of the toView.
if let move = toView.snapshotView(afterScreenUpdates: true) {
move.frame = toView.frame
container.addSubview(move)
UIView.animate(withDuration: Constants.MainPage.navControllerDuration, delay: 0.0, usingSpringWithDamping: 1000, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
move.frame = container.frame;
fromView.frame = fromDestinationFrame;
}, completion: { (finished) in
if finished {
if !container.subviews.contains(toView) {
container.addSubview(toView)
}
toView.frame = container.frame
fromView.removeFromSuperview()
move.removeFromSuperview()
transitionContext.completeTransition(true)
}
})
}
}
}
Cheers.
Related
the code i'm using works just fine in swift for iPhone apps but not in the WatchKit 7.0 beta. the outlets and actions are different. I'm not sure what needs to change to make it work in WatchKit. please help!
import WatchKit
import Foundation
class InterfaceController: WKInterfaceController {
#IBOutlet var spinButton: WKInterfaceButton!
var isRotating = false
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
#IBAction func spinAction() {
if !isRotating {
// create a spin animation
let spinAnimation = CABasicAnimation()
// starts from 0
spinAnimation.fromValue = 0
// goes to 360 ( 2 * π )
spinAnimation.toValue = M_PI*2
// define how long it will take to complete a 360
spinAnimation.duration = 1
// make it spin infinitely
spinAnimation.repeatCount = Float.infinity
// do not remove when completed
spinAnimation.removedOnCompletion = false
// specify the fill mode
spinAnimation.fillMode = kCAFillModeForwards
// and the animation acceleration
spinAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
// add the animation to the button layer
spinButton.layer.addAnimation(spinAnimation, forKey: "transform.rotation.z")
} else {
// remove the animation
spinButton.layer.removeAllAnimations()
}
// toggle its state
isRotating = !isRotating
}
}
You are limited to a subset of all the APIs available on iOS when developing for the watchOS.
If you want to do basic animations try out a WKInterfacePicker and change images when the digital crown is moved.
IBOutlet WKInterfacePicker *myPicker;
- (void)willActivate {
[super willActivate];
WKPickerItem *item1 = [[WKPickerItem alloc] init];
item1.contentImage = [WKImage imageWithImageName:#"Unknown.png"];
WKPickerItem *item2 = [[WKPickerItem alloc] init];
item2.contentImage = [WKImage imageWithImageName:#"Unknown-2.png"];
[self.myPicker setItems:array];
}
When the value exceeds the array count start over from index 0.
- (IBAction)myPickerAction:(NSInteger)value {
if (value % 2 == 0) {
[self.myPicker setSelectedItemIndex:-1];
}
}
This will make the WKInterfacePicker change between your images when the digital crown is rotated.
In my UISplitViewController, the master view is a UINavigationController containing a UITableViewController. Sometime, when the user selects an item in the table, I have to push another UITableViewController over the existing table in the master view.
In iOS 7, inside my first UITableViewController I just call
[self.navigationController pushViewController:otherTableVC animated:YES];
In iOS 8:
When the split view is collapsed, the otherTableVC becomes the detail View!
Then after rotating the device, we see the two tables side by side...
Worse, if the device shows the two panes, the code works great and the second table is pushed over the first one in the master view. But, after a double rotation, the two tables are again side by side. It seems the collapsed mode of the UISplitViewController interferes with my own navigation controller…
How can I manage my own UINavigationController in the Master View?
Thank you
EDITED:
My both primary and details views have a navigation Controller. And to solve my problem, I just discovered that, in collapsed mode, I have to create an extra Navigation Controller and push it over the primary navigation controller.
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:otherTableVC];
[self.navigationController pushViewController:navController animated:YES];
So I just discovered hat we can push a navigation Controller inside another Navigation Controller.
Short answer, you can control this behaviour via the UISplitViewControllerDelegate methods:
splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
splitViewController:separateSecondaryViewControllerFromPrimaryViewController:
I suspect what you really want to do is deal with the situation where you have an iOS 8 UISplitViewController-based app where your primary and detailed views are both UINavigationControllers and there are some viewControllers (within these navigation controllers) that you want to appear only on the primary or detail side of the split view. The answer below deals with this. It also copes with the situation where you sometimes wish for a view to replace the views in the Detail navigation controller, rather than getting pushed there.
A small caveat: the code below does not deal with all possible cases and has some assumptions:
We don't expect anything can change on the Detailed navigation controller stack when the split view is collapsed and those views are obscured by a detailed view above them.
Our UIViewController subclasses all have a shouldDisplayInDetailedView and shouldReplaceDetailedView property
We assume that we only push views onto the detailed navigation controller that have the shouldDisplayInDetailedView property set.
View controllers are added to the Detail side via splitViewController:showDetailViewController: or a pushViewController:animated: on the navigationController property of a view within a detailed view (in either expanded or collapsed state).
View controllers that should replace the view controllers in the Detail navigation controller are only added via splitViewController:showDetailViewController: and only from interaction with view in the Primary view controller, i.e., this can only happen if the Primary view controller is not obscured when in collapsed state.
We have a BlankViewController to display in the Detail View when the split view controller gets expanded but we only have view controllers that should stay on the Primary side.
I don't recommend implementing only one side of the splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: / splitViewController: separateSecondaryViewControllerFromPrimaryViewController: logic and depending on the default implementation for the other side. Apple do some strange things like putting the UINavigationViewController from the Detail side into the primary side as one of the viewControllers in the Primary navigation controller stack but then pushing other view controllers above it, which even if you understand completely still can't be replicated from your own code. Thus its best to handle both sides of the process yourself.
This is what I use:
#pragma mark -
#pragma mark Split View Controller delegate.
- (BOOL)splitViewController:(UISplitViewController *)splitViewController showViewController:(UIViewController *)vc sender:(id)sender
{
//Standard behaviour. This won't get called in our case when the split view is collapsed and the primary view controllers are obscured.
return NO;
}
// Since we treat warnings as errors, silence warning about unknown selector below on UIViewController subclasses.
#pragma GCC diagnostic ignored "-Wundeclared-selector"
- (BOOL)splitViewController:(UISplitViewController *)splitViewController showDetailViewController:(UIViewController *)vc sender:(id)sender
{
if (splitViewController.collapsed == NO)
{
// The navigation controller we'll be adding the view controller vc to.
UINavigationController *navController = splitViewController.viewControllers[1];
UIViewController *topDetailViewController = [navController.viewControllers lastObject];
if ([topDetailViewController isKindOfClass:[BlankViewController class]] ||
([vc respondsToSelector:#selector(shouldReplaceDetailedView)] && [vc performSelector:#selector(shouldReplaceDetailedView)]))
{
// Replace the (expanded) detail view with this new view controller.
[navController setViewControllers:#[vc] animated:NO];
}
else
{
// Otherwise, just push.
[navController pushViewController:vc animated:YES];
}
}
else
{
// Collapsed. Just push onto the conbined primary and detailed navigation controller.
UINavigationController *navController = splitViewController.viewControllers[0];
[navController pushViewController:vc animated:YES];
}
// We've handled this ourselves.
return YES;
}
- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController
{
UINavigationController *primaryNavController = (UINavigationController *)primaryViewController;
UINavigationController *secondaryNavController = (UINavigationController *)secondaryViewController;
UIViewController *bottomSecondaryView = [secondaryNavController.viewControllers firstObject];
if ([bottomSecondaryView isKindOfClass:[BlankViewController class]])
{
NSAssert([secondaryNavController.viewControllers count] == 1, #"BlankViewController is not only detail view controller");
// If our secondary controller is blank, do the collapse ourself by doing nothing.
return YES;
}
// We need to shift these view controllers ourselves.
// This should be the primary views and then the detailed views on top.
// Otherwise the UISplitViewController does wacky things like embedding a UINavigationController inside another UINavigation Controller, which causes problems for us later.
NSMutableArray *newPrimaryViewControllers = [NSMutableArray arrayWithArray:primaryNavController.viewControllers];
[newPrimaryViewControllers addObjectsFromArray:secondaryNavController.viewControllers];
primaryNavController.viewControllers = newPrimaryViewControllers;
return YES;
}
- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController
{
UINavigationController *primaryNavController = (UINavigationController *)primaryViewController;
// Split up the combined primary and detail navigation controller in their component primary and detail view controller lists, but with same ordering.
NSMutableArray *newPrimaryViewControllers = [NSMutableArray array];
NSMutableArray *newDetailViewControllers = [NSMutableArray array];
for (UIViewController *controller in primaryNavController.viewControllers)
{
if ([controller respondsToSelector:#selector(shouldDisplayInDetailedView)] && [controller performSelector:#selector(shouldDisplayInDetailedView)])
{
[newDetailViewControllers addObject:controller];
}
else
{
[newPrimaryViewControllers addObject:controller];
}
}
if (newDetailViewControllers.count == 0)
{
// If there's no detailed views on the top of the navigation stack, return a blank view (in navigation controller) for detailed side.
UINavigationController *blankDetailNavController = [[UINavigationController alloc] initWithRootViewController:[[BlankViewController alloc] init]];
return blankDetailNavController;
}
// Set the new primary views.
primaryNavController.viewControllers = newPrimaryViewControllers;
// Return the new detail navigation controller and views.
UINavigationController *detailNavController = [[UINavigationController alloc] init];
detailNavController.viewControllers = newDetailViewControllers;
return detailNavController;
}
Swift 4 Version with minor changes to make it work with my code:
func splitViewController(_ splitViewController: UISplitViewController, showDetail vc: UIViewController, sender: Any?) -> Bool {
if !isCollapsed {
// in expanded mode set new VC as top view controller of the detail nav controller
if let detailNavigationController = viewControllers[1] as? UINavigationController {
detailNavigationController.setViewControllers([vc], animated: false)
}
} else {
// in collapsed mode push the new view controller on the master nav controller
if let masterNavigationController = viewControllers[0] as? UINavigationController {
masterNavigationController.pushViewController(vc, animated: true)
}
}
return true
}
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
let masterNavigationController = primaryViewController as? UINavigationController
let detailNavigationController = secondaryViewController as? UINavigationController
let episodeDetailViewController = detailNavigationController?.viewControllers.first as? EpisodeDetailTableViewController
if episodeDetailViewController?.episode == nil {
// detail view is blank. We do not need to push this onto the master
return true
}
guard var newMasterViewControllers = masterNavigationController?.viewControllers else { return false }
newMasterViewControllers.append(contentsOf: detailNavigationController?.viewControllers ?? [])
masterNavigationController?.setViewControllers(newMasterViewControllers, animated: false)
return true
}
func splitViewController(_ splitViewController: UISplitViewController, separateSecondaryFrom primaryViewController: UIViewController) -> UIViewController? {
let masterNavigationViewController = primaryViewController as? UINavigationController
var newMasterViewControllers = [UIViewController]()
var newDetailViewControllers = [UIViewController]()
for vc in masterNavigationViewController?.viewControllers ?? [] {
if vc is PodcastsTableViewController || vc is EpisodesTableViewController {
newMasterViewControllers.append(vc)
} else {
newDetailViewControllers.append(vc)
}
}
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let detailNavigationController = storyboard.instantiateViewController(withIdentifier: "splitViewDetailViewController") as! UINavigationController
if newDetailViewControllers.count == 0 {
let emptyEpisodeDetailViewController = storyboard.instantiateViewController(withIdentifier: "episodeDetail")
newDetailViewControllers.append(emptyEpisodeDetailViewController)
}
masterNavigationViewController?.setViewControllers(newMasterViewControllers, animated: false)
detailNavigationController.setViewControllers(newDetailViewControllers, animated: false)
return detailNavigationController
}
In my application. I get a bunch of image URLs on the main thread like this:
- (void)viewDidLoad
{
[super viewDidLoad];
[self populateCollection];
}
- (void)populateCollection
{
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:[MTGJSONBridge JSONURLWithSetCode:#"LEA"]]
options:kNilOptions
error:nil];
NSLog(#"Json: %#", json);
NSArray *cards = json[#"cards"];
_URLs = [NSMutableArray arrayWithCapacity:cards.count];
for (NSDictionary *card in cards)
{
NSURL *imageURL = [MTGJSONBridge URLWithSetCode:#"LEA" cardName:card[#"imageName"]];
if (imageURL)
[_URLs addObject:imageURL];
}
}
This gets me about 300 URLs in 0.2 seconds. Then I try to load the images from each URL asynchronously like this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"reuseIdentifier";
MTGCardCollectionCell *cell = (MTGCardCollectionCell *)[collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
NSURL *url = _URLs[indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// No explicit autorelease pool needed here.
// The code runs in background, not strangling
// the main run loop.
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
dispatch_sync(dispatch_get_main_queue(), ^{
// This will be called on the main thread, so that
// you can update the UI, for example.
cell.imageView.image = image;
});
});
return cell;
}
I also have this:
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return _URLs.count;
}
The collection loads really quickly, and asynchronously too (I can scroll while things are loading, and I see new images pop up). The problem is that as I scroll up and down, even after all the images have loaded, the thing keeps rearranging itself in a random order: I'll be looking at one cell, and then it'll have its image switched with another for no apparent reason. Why is this happening?
I've discovered a very easy solution:
if (cell.imageView.image == nil)
{
NSURL *url = _URLs[indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// No explicit autorelease pool needed here.
// The code runs in background, not strangling
// the main run loop.
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
dispatch_sync(dispatch_get_main_queue(), ^{
// This will be called on the main thread, so that
// you can update the UI, for example.
cell.imageView.image = image;
});
});
}
else
{
NSLog(#"Cell image isn't nil");
}
All I have to do is check if the cell isn't already loaded. Turns out the - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath method is called whenever a cell comes into view. Even if it was just in the view.
I'm testing an RSS on my iPhone. It uses 0 nib files. I'll try to describe it as best as I can, and will post code if its required, but I bet its a common phenomena with a common solution. The issue is in a tableviewcontroller, and the solution probably needs to be implemented in the CellForRowAtIndexPath method. If I scroll down, preview images stay in their respective spots until the async queue loads the correct image for that cell. So if I have an image for array item 1, and I scroll down to array item 20, the image for array item 1 will still be there until my queue catches up and loads that image. How can I release the images from cells that I am not viewing? Thank you for your time.
Here is my CellForRow...
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier];
}
ArticleItem *object = _objects[indexPath.row];
cell.primaryLabel.text = object.title;
cell.secondaryLabel.text = object.strippedDescription;
cell.primaryLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.primaryLabel.numberOfLines = 0;
cell.primaryLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.secondaryLabel.numberOfLines = 0;
//Async dispatch queue for image preview loading...
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *preview = nil;
if (object.iG = nil)
{
preview = [UIImage imageNamed:#"CellLogo.png"];
}
else
{
preview = [UIImage imageWithData:object.iG];
}
dispatch_sync(dispatch_get_main_queue(), ^{
[[cell myImageView] setImage:preview];
[cell setNeedsLayout];
});
});
return cell;
}
If you gather, I have an ArticleItem class which pulls the image URLS and turns them into data , and I have a CustomCell class which does what its called.
CustomCell.h
#interface CustomCell : UITableViewCell {
UIImageView *myImageView;
}
#property(nonatomic,strong)UIImageView *myImageView;
#end
=====================================================
CustomCell.m
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {
// Initialization code
myImageView = [[UIImageView alloc]init];
[self.contentView addSubview:myImageView];
}
return self;
}
-(void)viewDidUnload {
myImageView = nil;
primaryLabel = nil;
secondaryLabel = nil;
}
Implement a subclass of UITableViewcell, make a property for imageview. As soon as it gets away from visibility, it will be released. Describing just the overview as you may yourself need to see the usage upon scrolling.
I use this code to draw an NSString on a UILabel(which i have subclassed already) with Core Graphics from a queue but nothing shows on screen. I get no error.
I call the function like this from a queue.
[label1 createtext];
-(void)createtext
{
UIFont *font=[UIFont fontWithName:#"Times New Roman" size:15.0];
//Core Text (Create Attributed String)
NSMutableArray *Text=[globaltextarray Text];
CGSize size=[[Text objectAtIndex:0] sizeWithFont:font];
if (UIGraphicsBeginImageContextWithOptions != NULL)
UIGraphicsBeginImageContextWithOptions(size,NO,0.0);
else
// iOS is < 4.0
UIGraphicsBeginImageContext(size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
if (ctx != NULL)
NSLog(#"not null context");
UIColor *textColor = [UIColor blackColor];
CGColorRef color = textColor.CGColor;
CTFontRef font = CTFontCreateWithName((CFStringRef) #"TimesNewRomanPSMT", 15.0, NULL);
CTTextAlignment theAlignment = kCTLeftTextAlignment;
CFIndex theNumberOfSettings = 1;
CTParagraphStyleSetting theSettings[1] = {
{ kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment),
&theAlignment }
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(theSettings, theNumberOfSettings);
NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:(id)font, (NSString *)kCTFontAttributeName,color, (NSString *)kCTForegroundColorAttributeName,paragraphStyle, (NSString *) kCTParagraphStyleAttributeName,nil];
NSAttributedString *stringToDraw = [[NSAttributedString alloc] initWithString:[NSString stringWithCString:[[Text objectAtIndex:indexpath1.row] cStringUsingEncoding:NSISOLatin1StringEncoding] encoding:NSUTF8StringEncoding] attributes:attributesDict];
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)stringToDraw);
//Prepare our view for drawing
CGContextSetTextMatrix(ctx, CGAffineTransformIdentity);
CGContextTranslateCTM(ctx, 0, ([self bounds]).size.height );
CGContextScaleCTM(ctx, 1.0, -1.0);
//Create Frame
CGMutablePathRef path = CGPathCreateMutable();
CGRect rect = self.frame;
CGPathAddRect(path, NULL, rect);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
//Draw Frame
CTFrameDraw(frame, ctx);
UIGraphicsEndImageContext();
//Release all retained objects
CFRelease(framesetter);
CFRelease(path);
[stringToDraw release];
}
Any help appreciated!
You are creating a temporary graphics context, drawing into it, then discarding it. You never tell UIKit to put anything on the screen.
You need to also:
create an image from the content of that context, using UIGraphicsGetImageFromCurrentImageContext
on the main thread, put that image into a view, or draw the image into another view
For instance:
// In your class's declaration, define an image view to show your drawing.
// Make sure that this view is created (either as part of a storyboard/nib, or with explicit code)
#property (nonatomic) UIImageView *imageView;
- (void)createText
{
CGSize size = /* you decide */;
UIGraphicsBeginImageContext(size);
// draw into the graphics context
// after you're done:
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
}
You are not drawing with the CoreGraphics framework, you are drawing with UIKit and that is not allowed in a background thread. You must only call UIKit methods from the main thread. For example, you are calling UIGraphicsBeginImageContext() but that is part of UIKit. You are unlikely to get any benefit from rendering widget code in a background thread anyway, unless you are doing some truly disconnected offline rendering (like creating a PDF or something) then you should just render your widgets in the main thread.