Xcode, Obj-c, iOS 5.2 iOS 6.0
I have a UISegmentedControl which is being inserted into the bottom toolbar using something like this:
-(void) makeSegmentedControl {
self.SegControl = [[UISegmentedControl alloc] initWithItems:#[#"Title1",#"Title2"]];
self.SegControl.segmentedControlStyle = UISegmentedControlStyleBar;
[self.SegControl setTarget:self action:#selector(handleSegment) forControlEvents:UIControlEventValueChanged]
[self.SegControl setSelectedSegmentIndex:0];
}
-(void) AddSegmentedControlToToolbar {
UIBarButtonItem *tmp = [UIBarButtonItem new];
[tmp setCustomView:self.SegControl];
[self.navigationController.toolbar setItems#[tmp]];
}
-(void) tintSegmentedControl {
for (int i = 0; i< [self.SegControl.subviews count]; i++) {
[[self.SegControl.subviews objectAtIndex:i] setTint:[UIcolor greenColor];
}
NSArray * sortedViews = [self.SegControl.subviews sortArrayUsingFunction:sortFunction];
[[sortedViews objectAtIndex:self.SegControl.selectedSegmentIndex] setTintColor:[UIColor lightGreenColor]];
for (id view in self.SegControl.subviews) {
[view removeFromSuperView];
}
for (id view in sortedViews) {
[self.SegControl addSubview:view];
}
}
-(void)viewDidLoad {
[self makeSegmentedControl];
[self addSegmentedControlToToobar];
[self tintSegmentedControl];
}
-(void) handleSegment {
[self tintSegmentedControl];
}
When I run the similar code in our app it shows a very BLUE UISegmentedController which turns GREEN on clicking. I've tried a few things and it seems that the tint on the UISegments just refuses to stick until after the view has finished loading. Any idea what's happening here?
Edit
So, it turns out I was actually using the iOS 6.0 simulator and there is a restriction in iOS 6 that you can't tint a UISegmentedController before ViewDidAppear:Animated: has run. I think that this was the problem, because when I make this change:
-(void)viewDidLoad {
[self makeSegmentedControl];
[self addSegmentedControlToToobar];
}
-(void)viewDidAppear {
[NSTimer timerWithTimeInterval:0 target:self selector:#selector(tintSegmentedControl) userInfo:nil repeats:NO];
}
it works. Not 100% sure what's going on. Is there an easier way?
try this code
-(void) tintSegmentedControl {
[self.SegControl setTintColor:[UIColor greenColor];
NSArray * sortedViews = [self.SegControl.subviews sortArrayUsingFunction:sortFunction];
[[sortedViews objectAtIndex:self.SegControl.selectedSegmentIndex] setTintColor:[UIColor lightGreenColor]];
for (id view in self.SegControl.subviews) {
[view removeFromSuperView];
}
for (id view in sortedViews) {
[self.SegControl addSubview:view];
}
}
So, it turns out there is a restriction in iOS 6 that you can't tint a UISegmentedController before ViewDidAppear:Animated: has run. To circumvent it I made this code change (as you can see in the edit).
-(void)viewDidLoad {
[self makeSegmentedControl];
[self addSegmentedControlToToobar];
}
-(void)viewDidAppear {
[NSTimer timerWithTimeInterval:0 target:self selector:#selector(tintSegmentedControl) userInfo:nil repeats:NO];
}
Related
I am using a Toggle button in XIB,
in AwakeFromNib :I set title and BG image of the button.
Code works very well till OSX 10.10.5 and below but on higher versions when I click on button the text gets removed.
Set attributed Title
[self.deleteButton setWantsLayer:YES];
self.deleteButton.layer.backgroundColor = [self setButtonBGColor];
[self setButtonTitleFor:self.deleteButton
toString:#"Delete"];
[self.deleteButton setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay];
- (void)setButtonTitleFor:(NSButton*)button toString:(NSString*)title
{
NSAttributedString *attrString = [self getAttributedStringForString:title];
[button setAttributedTitle:attrString];
}
Any idea hat should be done.
So finally got it right
Subclassing NSButtonCell helped me out
RPButtonTextTopCell.h
#interface RPButtonTextTopCell : NSButtonCell
#end
RPButtonTextTopCell.m
#implementation RPButtonTextTopCell
-(id) init
{
self = [super init];
if (self) {
}
return self;
}
-(id) initWithCoder:(NSCoder *)decoder
{
return [super initWithCoder:decoder];
}
-(id) initTextCell:(NSString *)string
{
return [super initTextCell:string];
}
-(NSRect)titleRectForBounds:(NSRect)theRect
{
NSRect titleFrame = [super titleRectForBounds:theRect];
NSSize titleSize = [[self attributedStringValue] size];
titleFrame.origin.y = (theRect.origin.y - (theRect.size.height-titleSize.height)*0.5) + 5;
return titleFrame;
}
#end
Utilizing This Custom Class
RPButtonTextTopCell *deleteCell = [[RPButtonTextTopCell alloc] init];
deleteCell.backgroundColor = [self setNSButtonBGColor];
[self.deleteButton setCell:deleteCell];
[self setButtonTitleFor:self.deleteButton
toString:#"Delete"];
[self.deleteButton setAction:#selector(deleteButtonClicked:)];
[self.deleteButton setTarget:self];
and its solved....
I have a problem with AVAudioPlayer in background, everything works but when I have the controls in the lock screen and I click on the pause button the control receive the event but the lock screen with controls disappears. I want that lock screen remain with controls like Music app.
I put these lines in viewDidLoad:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
and then :
-(void)viewDidAppear:(BOOL)animated{
[[UIApplication sharedApplication]beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
-(BOOL)canBecomeFirstResponder{ return YES; }
- (void)viewWillDisappear:(BOOL)animated {
//End recieving events
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
//if it is a remote control event handle it correctly
if (event.type == UIEventTypeRemoteControl) {
if (event.subtype == UIEventSubtypeRemoteControlPause) {
[self.playButton setBackgroundImage:[UIImage imageNamed:#"play_icon.png"]
forState:UIControlStateNormal];
[self.audioPlayer pauseAudio];
self.isPaused = FALSE;
}
else if (event.subtype == UIEventSubtypeRemoteControlPlay){
if (!self.isPaused) {
[self.playButton setBackgroundImage:[UIImage imageNamed:#"pause.png"]
forState:UIControlStateNormal];
//start a timer to update the time label display
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateTime:)
userInfo:nil
repeats:YES];
[self.audioPlayer playAudio];
self.isPaused = TRUE;
}
}
}
}
I find the solution !!
Using this property :
#property MPNowPlayingInfoCenter* nowPlayingInfo;
I have to add these lines on my viewDidLoad :
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo=[NSDictionary dictionaryWithObject:#1.0f forKey:MPNowPlayingInfoPropertyPlaybackRate];
So I have a sprite kit game and I'm adding music to it. The music worked fine and worked until I decided to add a slider to control the music volume. So to do this I added another scene for the "options menu." The problem is, when I exit the scene to go to the actual game interface, the game music completely disappears. Also I'm having trouble removing the actual slider. My code for the options scene will be listed down below. (It is still in the making) I'm new to this website so I'm sorry if the code is messed up.
#interface OptionsScene ()
#property BOOL contentCreated;
#end
#implementation OptionsScene
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
self.anchorPoint = CGPointMake(0.5, 0.5);
}
return self;
}
-(void)didMoveToView:(SKView *)view
{
NSString *music = [[NSBundle mainBundle] pathForResource:#"InAmberClad" ofType:#"mp3"];
backGroundMusic = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:music] error:NULL];
backGroundMusic.delegate = self;
backGroundMusic.numberOfLoops = -1;
[backGroundMusic play];
CGRect frame = CGRectMake(200, 300, 150, 10);
_volumeSlider = [[UISlider alloc] initWithFrame:frame];
[_volumeSlider addTarget:self action:#selector(volumeControl) forControlEvents:UIControlEventValueChanged];
_volumeSlider.maximumValue = 1;
_volumeSlider.minimumValue = 0;
_volumeSlider.continuous = YES;
[_volumeSlider setValue:0.5];
[_volumeSlider setBackgroundColor:[UIColor blackColor]];
if (!self.contentCreated) {
[self.view addSubview:_volumeSlider];
[self createContents];
self.contentCreated = YES;
}
}
-(void)createContents
{
self.scaleMode = SKSceneScaleModeAspectFill;
}
-(void)volumeControl
{
[backGroundMusic setVolume:_volumeSlider.value];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
SKScene *options = [[MyScene alloc] initWithSize:self.size];
[self.view presentScene:options];
}
#end
I think if you don't delegate the backgroundMusic to self, this problem will be solved. Also, before play the music, run a [backGroundMusic prepareToPlay]; will improve performance.
Here is a link to a tutorial, on page search for "Music" to see how he did it.
Ray Wenderlich
I am back again with my pigs (means cochon in French)! :)
I would like to created a at simple animated image with an NSTimer and I can't find example of it not with the iphone SDK.
after 3 second, the debugger displays : Program received signal: “EXC_BAD_ACCESS”
here is the code, if you have some time to check it and say what you think of it..
#import <Cocoa/Cocoa.h>
#interface maatView : NSView {
NSArray *mesImagesCochon;
NSImageView * monCochon;
int counter;
}
-(void)tick;
#end
here is the .m file:
#import "maatView.h"
#implementation maatView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)awakeFromNib
{
counter=0;
mesImagesCochon = [NSArray arrayWithObjects:
[NSImage imageNamed:#"cochon.png"],
[NSImage imageNamed:#"cochon2.png"],
[NSImage imageNamed:#"cochon3.png"],
[NSImage imageNamed:#"cochon4.png"],
nil];
[NSTimer scheduledTimerWithTimeInterval:3
target:self
selector:#selector(tick)
userInfo:nil
repeats:NO];
monCochon = [[NSImageView alloc]init];
[monCochon setFrame:NSMakeRect(0, 0, 100, 100)];
}
- (void)drawRect:(NSRect)dirtyRect {
// Drawing code here.
}
-(void)tick
{
NSLog(#"method appele %d",mesImagesCochon.count);
counter++;
if (counter > mesImagesCochon.count)
{
counter = 0;
}
[monCochon setImage:[mesImagesCochon objectAtIndex:counter]];
[self addSubview:monCochon];
}
#end
I'm making a little server app for OS X and I'm using an NSTextView to log some info about connected clients.
Whenever I need to log something I'm appending the new message to the text of the NSTextView this way:
- (void)logMessage:(NSString *)message
{
if (message) {
self.textView.string = [self.textView.string stringByAppendingFormat:#"%#\n",message];
}
}
After this I'd like the NSTextField (or maybe I should say the NSClipView that contains it) to scroll down to show the last line of its text (obviously it should scroll only if the last line is not visible yet, in fact if then new line is the first line I log it is already on the screen so there is no need to scroll down).
How can I do that programmatically?
Found solution:
- (void)logMessage:(NSString *)message
{
if (message) {
[self appendMessage:message];
}
}
- (void)appendMessage:(NSString *)message
{
NSString *messageWithNewLine = [message stringByAppendingString:#"\n"];
// Smart Scrolling
BOOL scroll = (NSMaxY(self.textView.visibleRect) == NSMaxY(self.textView.bounds));
// Append string to textview
[self.textView.textStorage appendAttributedString:[[NSAttributedString alloc]initWithString:messageWithNewLine]];
if (scroll) // Scroll to end of the textview contents
[self.textView scrollRangeToVisible: NSMakeRange(self.textView.string.length, 0)];
}
As of OS 10.6 it's as simple as nsTextView.scrollToEndOfDocument(self).
Swift 4 + 5
let smartScroll = self.textView.visibleRect.maxY == self.textView.bounds.maxY
self.textView.textStorage?.append("new text")
if smartScroll{
self.textView.scrollToEndOfDocument(self)
}
I've been messing with this for a while, because I couldn't get it to work reliably. I've finally gotten my code working, so I'd like to post it as a reply.
My solution allows you to scroll manually, while output is being added to the view. As soon as you scroll to the absolute bottom of the NSTextView, the automatic scrolling will resume (if enabled, that is).
First a category to #import this only when needed...
FSScrollToBottomExtensions.h:
#interface NSView (FSScrollToBottomExtensions)
- (float)distanceToBottom;
- (BOOL)isAtBottom;
- (void)scrollToBottom;
#end
FSScrollToBottomExtensions.m:
#implementation NSView (FSScrollToBottomExtensions)
- (float)distanceToBottom
{
NSRect visRect;
NSRect boundsRect;
visRect = [self visibleRect];
boundsRect = [self bounds];
return(NSMaxY(visRect) - NSMaxY(boundsRect));
}
// Apple's suggestion did not work for me.
- (BOOL)isAtBottom
{
return([self distanceToBottom] == 0.0);
}
// The scrollToBottom method provided by Apple seems unreliable, so I wrote this one
- (void)scrollToBottom
{
NSPoint pt;
id scrollView;
id clipView;
pt.x = 0;
pt.y = 100000000000.0;
scrollView = [self enclosingScrollView];
clipView = [scrollView contentView];
pt = [clipView constrainScrollPoint:pt];
[clipView scrollToPoint:pt];
[scrollView reflectScrolledClipView:clipView];
}
#end
... create yourself an "OutputView", which is a subclass of NSTextView:
FSOutputView.h:
#interface FSOutputView : NSTextView
{
BOOL scrollToBottomPending;
}
FSOutputView.m:
#implementation FSOutputView
- (id)setup
{
...
return(self);
}
- (id)initWithCoder:(NSCoder *)aCoder
{
return([[super initWithCoder:aCoder] setup]);
}
- (id)initWithFrame:(NSRect)aFrame textContainer:(NSTextContainer *)aTextContainer
{
return([[super initWithFrame:aFrame textContainer:aTextContainer] setup]);
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (void)awakeFromNib
{
NSNotificationCenter *notificationCenter;
NSView *view;
// viewBoundsDidChange catches scrolling that happens when the caret
// moves, and scrolling caused by pressing the scrollbar arrows.
view = [self superview];
[notificationCenter addObserver:self
selector:#selector(viewBoundsDidChangeNotification:)
name:NSViewBoundsDidChangeNotification object:view];
[view setPostsBoundsChangedNotifications:YES];
// viewFrameDidChange catches scrolling that happens because text
// is inserted or deleted.
// it also catches situations, where window resizing causes changes.
[notificationCenter addObserver:self
selector:#selector(viewFrameDidChangeNotification:)
name:NSViewFrameDidChangeNotification object:self];
[self setPostsFrameChangedNotifications:YES];
}
- (void)handleScrollToBottom
{
if(scrollToBottomPending)
{
scrollToBottomPending = NO;
[self scrollToBottom];
}
}
- (void)viewBoundsDidChangeNotification:(NSNotification *)aNotification
{
[self handleScrollToBottom];
}
- (void)viewFrameDidChangeNotification:(NSNotification *)aNotification
{
[self handleScrollToBottom];
}
- (void)outputAttributedString:(NSAttributedString *)aAttributedString
flags:(int)aFlags
{
NSRange range;
BOOL wasAtBottom;
if(aAttributedString)
{
wasAtBottom = [self isAtBottom];
range = [self selectedRange];
if(aFlags & FSAppendString)
{
range = NSMakeRange([[self textStorage] length], 0);
}
if([self shouldChangeTextInRange:range
replacementString:[aAttributedString string]])
{
[[self textStorage] beginEditing];
[[self textStorage] replaceCharactersInRange:range
withAttributedString:aAttributedString];
[[self textStorage] endEditing];
}
range.location += [aAttributedString length];
range.length = 0;
if(!(aFlags & FSAppendString))
{
[self setSelectedRange:range];
}
if(wasAtBottom || (aFlags & FSForceScroll))
{
scrollToBottomPending = YES;
}
}
}
#end
... You can add a few more convenience methods to this class (I've stripped it down), so that you can output a formatted string.
- (void)outputString:(NSString *)aFormatString arguments:(va_list)aArguments attributeKey:(NSString *)aKey flags:(int)aFlags
{
NSMutableAttributedString *str;
str = [... generate attributed string from parameters ...];
[self outputAttributedString:str flags:aFlags];
}
- (void)outputLineWithFormat:(NSString *)aFormatString, ...
{
va_list args;
va_start(args, aFormatString);
[self outputString:aFormatString arguments:args attributeKey:NULL flags:FSAddNewLine];
va_end(args);
}
I have some customised NSTextView and custom input method so my option was to use:
self.scrollView.contentView.scroll(NSPoint(x: 1, y: self.textView.frame.size.height))