How to customize the color of the navigation bar in qlpreviewcontroller - navigationbar

Can I customize the color of the navigation bar in the QlPreviewController controller?
I have tried following
[[UINavigationBar appearanceWhenContainedIn: [QLPreviewController class], nil] setBarTintColor: [UIColor redColor]];
but it does not work.
Thanks.

Yeah, there is a bug with barTintColor on QLPreviewController for iOS 11 if you are showing it via presentViewController: animated:
Here's my solution, use setBackgroundImage: with 1x1 image instead of setBarTintColor:
[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:#[[QLPreviewController class]]]
setBackgroundImage:[UIImage imageWithColor:[UIColor redColor]]
forBarMetrics:UIBarMetricsDefault];
And imageWithColor: is a method in my custom category of UIImage which is returning resizable 1x1 image of the desired color (red color in the example above):
+ (UIImage *)imageWithColor:(UIColor *)color {
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
const CGFloat alpha = CGColorGetAlpha(color.CGColor);
const BOOL opaque = alpha == 1;
UIGraphicsBeginImageContextWithOptions(rect.size, opaque, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
I also suggest to wrap this with iOS version check like:
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"11.0")) {
[[UINavigationBar appearance...
setBackgroundImage:[UIImage imageWithColor:...]
forBarMetrics:UIBarMetricsDefault];
}
Where SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO is from:
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

Thanks to Aleksander Lashevich for his answer! Works like a charm. I converted it to Swift 4 for convenience.
let navbar = UINavigationBar.appearance(whenContainedInInstancesOf: [QLPreviewController.self])
navbar.setBackgroundImage(self.imageWithColor(color: UIColor.red), for: UIBarMetrics.default)
And for the image generation:
func imageWithColor(color: UIColor) -> UIImage {
let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
let alpha = color.cgColor.alpha
let opaque = alpha == 1
UIGraphicsBeginImageContextWithOptions(rect.size, opaque, 0)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(color.cgColor)
context?.fill(rect)
return UIGraphicsGetImageFromCurrentImageContext()!
}

If you guys don't want to use the appearance proxy for whatever reason there is another way to tackle this issue if you subclass the QLPreviewController. It turns out that if you present the QLPreviewController modally, it creates a UINavigationController which is then added as a child ViewController to the view hierarchy. This however does not happen on initialization or even before the viewDidLoad is called. It happens when the QLPreviewController is about to be presented. So overriding the viewWillAppear function in the QLPreviewController subclass is the way to go here:
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let nc = self.children.first as? UINavigationController {
// your customization code goes here
nc.navigationBar.tintColor = .yellow
}
}

I use this workaround because all others don't work for me.
import QuickLook
class PreviewController: QLPreviewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.setBackgroundImage(nil, for: .any, barMetrics: .default)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.isTranslucent = false //optional
}
}

Please use below code in App delegate
[[UINavigationBar appearance] setBarTintColor:#your color#];

Related

NSTextView inside NSTableView Blocks Scrolling

I have an NSTableView and within each table row there is an NSTextView. I have disabled scrolling on the NSTextView in my storyboard. I can scroll my NSTableView just fine, but when my mouse cursor is over the top of the NSTextView, the scrolling stops. I assume this is because the NSTextView is intercepting the scroll behavior.
The arrow points to the NSTextView:
Keep in mind that the NSTextView is in a subclassed NSTableViewCell.
class AnnouncementsVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
#IBOutlet weak var tableView: NSTableView!
override func viewDidLoad() {
//...
}
func numberOfRows(in tableView: NSTableView) -> Int {
//...
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
//...
}
}
class AnnouncementTableCell: NSTableCellView{
#IBOutlet weak var message: NSTextView!
}
How do I make the NSTextView pass its scroll events up to its parent NSTableView? My guess is I will need scrollWheel(with event: NSEvent) but I'm unsure where it goes since I have two different classes at play here.
Here's a Swift (4.2) version that works great:
class MyScrollClass: NSScrollView{
override func scrollWheel(with event: NSEvent) {
self.nextResponder?.scrollWheel(with: event)
}
}
Just add that sub-class your NSScrollView and it should work.
One way to achieve this would be to subclass NSScrollView and implement scrollWheel: to conditionally (1) either consume scroll-events in your subclass-instance as needed, or (2) forward them to an enclosing scrollview, based on your instances' current scrollposition.
Here's a rather rudimentary implementation (Obj-C) that i've used in the past:
- (void)scrollWheel:(NSEvent *)e {
if(self.enclosingScrollView) {
NSSize
contSize = self.contentSize,
docSize = [self.documentView bounds].size,
scrollSize = NSMakeSize(MAX(docSize.width - contSize.width, 0.0f),
MAX(docSize.height - contSize.height,0.0f) );
NSPoint
scrollPos = self.documentVisibleRect.origin,
normPos = NSMakePoint(scrollSize.width ? scrollPos.x/scrollSize.width : 0.0,
scrollSize.height ? scrollPos.y/scrollSize.height : 0.0 );
if( ((NSView*)self.documentView).isFlipped ) normPos.y = 1.0 - normPos.y;
if( ( e.scrollingDeltaX>0.0f && normPos.x>0.0f)
|| ( e.scrollingDeltaX<0.0f && normPos.x<1.0f)
|| ( e.scrollingDeltaY<0.0f && normPos.y>0.0f)
|| ( e.scrollingDeltaY>0.0f && normPos.y<1.0f) ) {
[super scrollWheel:e];
}
else
[self.nextResponder scrollWheel:e];
}
else
// Don't bother when not nested inside another NSScrollView
[super scrollWheel:e];
}
It still leaves much to be desired, like handling the deltaX and deltaY components independantly, but perhaps it's sufficient for your case.

SKNode scale from the touched point

I have added UIPinchGestureRecognizer to my scene.view to scale my content. I actually scale the parent node where all my visible contents reside. But I have problem though with scaling point. The thing is node scale from the lower-left corner. It's definitely not what I want. Do I have to write lots of code to be able to scale from the point where pinching occurs? Could you please give some hints as to what way to follow.
I have been working on the same problem and my solution is shown below. Not sure if it is the best way to do it, but so far it seems to work. I'm using this code to zoom in and out of an SKNode that has several SKSpriteNode children. The children all move and scale with the SKNode as desired. The anchor point for the scaling is the location of the pinch gesture. The parent SKScene and other SKNodes in the scene are not affected. All of the work takes place during recognizer.state == UIGestureRecognizerStateChanged.
// instance variables of MyScene.
SKNode *_mySkNode;
UIPinchGestureRecognizer *_pinchGestureRecognizer;
- (void)didMoveToView:(SKView *)view
{
_pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleZoomFrom:)];
[[self view] addGestureRecognizer:_pinchGestureRecognizer];
}
// Method that is called by my UIPinchGestureRecognizer.
- (void)handleZoomFrom:(UIPinchGestureRecognizer *)recognizer
{
CGPoint anchorPoint = [recognizer locationInView:recognizer.view];
anchorPoint = [self convertPointFromView:anchorPoint];
if (recognizer.state == UIGestureRecognizerStateBegan) {
// No code needed for zooming...
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint anchorPointInMySkNode = [_mySkNode convertPoint:anchorPoint fromNode:self];
[_mySkNode setScale:(_mySkNode.xScale * recognizer.scale)];
CGPoint mySkNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_mySkNode];
CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, mySkNodeAnchorPointInScene);
_mySkNode.position = CGPointAdd(_mySkNode.position, translationOfAnchorInScene);
recognizer.scale = 1.0;
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
// No code needed here for zooming...
}
}
The following are helper functions that were used above. They are from the Ray Wenderlich book on Sprite Kit.
SKT_INLINE CGPoint CGPointAdd(CGPoint point1, CGPoint point2) {
return CGPointMake(point1.x + point2.x, point1.y + point2.y);
}
SKT_INLINE CGPoint CGPointSubtract(CGPoint point1, CGPoint point2) {
return CGPointMake(point1.x - point2.x, point1.y - point2.y);
}
SKT_INLINE GLKVector2 GLKVector2FromCGPoint(CGPoint point) {
return GLKVector2Make(point.x, point.y);
}
SKT_INLINE CGPoint CGPointFromGLKVector2(GLKVector2 vector) {
return CGPointMake(vector.x, vector.y);
}
SKT_INLINE CGPoint CGPointMultiplyScalar(CGPoint point, CGFloat value) {
return CGPointFromGLKVector2(GLKVector2MultiplyScalar(GLKVector2FromCGPoint(point), value));
}
I have translated ninefifteen's solution for Swift and Pinch Gestures. I spent a couple days trying to get this to work on my own. Thank goodness for ninefifteen's Obj-C post! Here is the Swift version that appears to be working for me.
func scaleExperiment(_ sender: UIPinchGestureRecognizer) {
var anchorPoint = sender.location(in: sender.view)
anchorPoint = self.convertPoint(fromView: anchorPoint)
let anchorPointInMySkNode = _mySkNode.convert(anchorPoint, from: self)
_mySkNode.setScale(_mySkNode.xScale * sender.scale)
let mySkNodeAnchorPointInScene = self.convert(anchorPointInMySkNode, from: _mySkNode)
let translationOfAnchorInScene = (x: anchorPoint.x - mySkNodeAnchorPointInScene.x, y: anchorPoint.y - mySkNodeAnchorPointInScene.y)
_mySkNode.position = CGPoint(x: _mySkNode.position.x + translationOfAnchorInScene.x, y: _mySkNode.position.y + translationOfAnchorInScene.y)
sender.scale = 1.0
}
Can't zoom I don't know why but the main problem is those SKT_INLINE. I've googled them and didn't found anything about 'em... The problem is when I copy/paste them in my project the compiler tells me I have to add an ";" right after them. I wonder if that's the reason that I can zoom.
In Swift 4, my SKScene adds the UIPinchGestureRecognizer to the view but passes handling of the pinch gesture off to one of its SKNode children that is created in the scene's init(), due to some reasons not relevant here. Anyhow, this is ninefifteen's answer from the perspective of what s/he calls _mySkNode. It also includes a little code to limit the zoom and does not use the convenience functions listed at the bottom of his post. The #objc part of the declaration allows the function to be used in #selector().
Here is what is in my SKScene:
override func didMove(to view: SKView) {
let pinchRecognizer: UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self.grid, action: #selector(self.grid.pinchZoomGrid))
self.view!.addGestureRecognizer(pinchRecognizer)
}
And this is the relevant section in my SKNode:
// Pinch Management
#objc func pinchZoomGrid(_ recognizer: UIPinchGestureRecognizer){
var anchorPoint: CGPoint = recognizer.location(in: recognizer.view)
anchorPoint = self.scene!.convertPoint(fromView: anchorPoint)
if recognizer.state == .began {
// No zoom code
} else if recognizer.state == .changed {
let anchorPointInGrid = self.convert(anchorPoint, from: self.scene!)
// Start section that limits the zoom
if recognizer.scale < 1.0 {
if self.xScale * recognizer.scale < 0.6 {
self.setScale(0.6)
} else {
self.setScale(self.xScale * recognizer.scale)
}
} else if recognizer.scale > 1.0 {
if self.xScale * recognizer.scale > 1.5 {
self.setScale(1.5)
} else {
self.setScale(self.xScale * recognizer.scale)
}
}
// End section that limits the zoom
let gridAnchorPointInScene = self.scene!.convert(anchorPointInGrid, from: self)
let translationOfAnchorPointInScene = CGPoint(x:anchorPoint.x - gridAnchorPointInScene.x,
y:anchorPoint.y - gridAnchorPointInScene.y)
self.position = CGPoint(x:self.position.x + translationOfAnchorPointInScene.x,
y:self.position.y + translationOfAnchorPointInScene.y)
recognizer.scale = 1.0
} else if recognizer.state == .ended {
// No zoom code
}
}

ios 7 view with transparent content overlaps previous view

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.rootViewContro‌​ller = 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.

iOS table preview image release

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.

Core graphics draw NSString from background thread

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.

Resources