How to add NSTextField over SKScene? - objective-c

I am creating a game using Sprite Kit, want to add a text field over scene. Here is the code:
#interface MKMainMenuScene ()
//...
#property (nonatomic) NSTextField *field;
#end
#implementation MKMainMenuScene
- (void)didMoveToView:(SKView *)view
{
[super didMoveToView:view];
[self.view addSubview:_field];
}
- (id)initWithSize:(CGSize)size
{
if (self == [super initWithSize:size])
{
//...
_field = [[NSTextField alloc] initWithFrame:
NSMakeRect(self.frame.size.width / 2, self.frame.size.height / 2 + 20,
100, 40)];
[_field setBackgroundColor:[NSColor whiteColor]];
[_field setStringValue:#"Enter smth"];
}
return self;
}
But text field doesn't appear, it is under the scene. Does someone know what is the problem and how to solve it?

Got it working.
Must set necessary views as Layer-Backed Views. Tried doing this in the Nib, didn't work for me. So I did it in code.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
/* Insert Label. FOR TESTING, may remove */
NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(50, 50, 300, 300)];
[label setStringValue:#"Hello All"];
[label setTextColor:[NSColor whiteColor]];
[label setBackgroundColor:[NSColor redColor]];
[label setFont:[NSFont fontWithName:#"Helvetica" size:20]];
[[self.window contentView] addSubview:label];
/* SET LAYER BACK VIEW
* This is the one line code to make it work.
*/
[self.window.contentView setWantsLayer:YES];
}
UPDATE:
The above code will cause a freeze or a problem on either a SCNView or SKView. To fix this, don't set the UI as a subview, but as a sibling to SKView. And then set the UI as Layer-Backed by setWantsLayer.
Window
-> Content NSView
-> SKView/SCNView
-> NSTextField <-- Layer-Backed
Code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
/* Insert Label. FOR TESTING, may remove */
NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(50, 50, 300, 300)];
// . . . .
[[self.window contentView] addSubview:label];
/* UPDATE */
[label setWantsLayer:YES];
}
Hope this helps someone else who's following my steps.

Try this one:
- (void)didMoveToView:(SKView *)view
{
[super didMoveToView:view];
[view addSubview:_field];
}

Related

Updating programmatically set colors on Mode change (dark mode, light mode) on macOS (objective-c)

i am on macOS, objective-c, not iOS. XCode 12.
In a lot of views i set colors like this:
self.menuIconBar.wantsLayer = YES;
self.menuIconBar.layer.backgroundColor = [NSColor colorNamed:#"color_gradient_right"].CGColor;
Whenever the user changes the Appeareance, e.g. to Dark mode, i expect my colors to change according to the Asset setup:
Unfortunately, nothing happens. BUT: The same color applied in IB directly changes as expected. Still i'd need them to change programmatically too.
Then i tried to hook on notifications:
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:#selector(appleInterfaceThemeChangedNotification:) name:#"AppleInterfaceThemeChangedNotification" object:nil];
I receive the notifications, but when i then call the same code like above again, still the wrong color is loaded.
self.menuIconBar.layer.backgroundColor = [NSColor colorNamed:#"color_gradient_right"].CGColor;
Any help appreciated
The following example will change the background color of a custom view depending on the Appearance setting in System Preferences. It may be run in Xcode by creating an objc project, deleting the pre-existing App Delegate, and replacing the code in 'main.m' with the code below:
#import <Cocoa/Cocoa.h>
#interface CustomView : NSView
#end
#implementation CustomView
- (id)initWithFrame:(NSRect)frameRect {
if ((self = [super initWithFrame:frameRect]) != nil) {
// Add initialization code here
}
return self;
}
- (void)drawRect:(NSRect)rect {
}
- (void)viewDidChangeEffectiveAppearance {
NSLog (#"appearance did change.");
NSAppearance *changedAppearance = NSApp.effectiveAppearance;
NSAppearanceName newAppearance = [changedAppearance bestMatchFromAppearancesWithNames:#[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
NSLog (#"new appearance name = %#", newAppearance);
if([newAppearance isEqualToString:NSAppearanceNameDarkAqua]){
[[self layer] setBackgroundColor:CGColorCreateGenericRGB( 1.0, 0.0, 0.0, 1.0 )];
} else {
[[self layer] setBackgroundColor:CGColorCreateGenericRGB( 0.0, 0.0, 1.0, 1.0 )];
}
}
// Use this if you want 0,0 (origin) to be top, left
// Otherwise origin will be at bottom, left (Unflipped)
-(BOOL)isFlipped {
return YES;
}
#end
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
}
- (void) buildMenu;
- (void) buildWindow;
#end
#implementation AppDelegate
- (void) buildMenu {
NSMenu *menubar = [NSMenu new];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
[NSApp setMainMenu:menubar];
NSMenu *appMenu = [NSMenu new];
NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:#"Quit"
action:#selector(terminate:) keyEquivalent:#"q"];
[appMenu addItem:quitMenuItem];
[menuBarItem setSubmenu:appMenu];
}
- (void) buildWindow {
#define _wndW 600
#define _wndH 550
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH )
styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable
backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"Test window"];
[window makeKeyAndOrderFront: nil];
// **** CustomView **** //
CustomView *view = [[CustomView alloc]initWithFrame:NSMakeRect( 20, 60, _wndW - 40, _wndH - 80 )];
[view setWantsLayer:YES];
[[view layer] setBackgroundColor:CGColorCreateGenericRGB( 0.0, 0.0, 1.0, 1.0 )];
[[window contentView] addSubview:view];
// **** Quit btn **** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWindow];
}
- (void) applicationDidFinishLaunching: (NSNotification *)notification {
}
#end
int main() {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
return 0;
}
This worked for me in my NSView subclass:
- (void)awakeFromNib {
self.wantsLayer = YES;// might be unnecessary
}
- (void)viewDidChangeEffectiveAppearance {
self.needsDisplay = YES;
}
- (void)updateLayer {
self.layer.backgroundColor = NSColor.unemphasizedSelectedContentBackgroundColor.CGColor;
}
As of macOS 11 one should use the performAsCurrentDrawingAppearance: instance method and add anything to apply after an appearance change into the given block.

Xcode 6 Rounded Rect Button Issue

An attempt to programmatically create a rounded rectangular button in an Objective-C View Controller in Xcode 6 and iOS 8.1 only produces a non-rounded rect button. The UIButton buttonWithType: method is being used.
Why?
//
// ViewController.m
//
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)loadView // Called the first time the view property is accessed.
{
CGRect viewRect = [[UIScreen mainScreen] bounds];
UIView *view = [[UIView alloc] initWithFrame:viewRect];
self.view = view;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Set the view's background color.
self.view.backgroundColor = [UIColor colorWithRed:0.462 green:0.999 blue:0.999 alpha:1.0];
// Create a button.
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
// Place and size the button on the screen.
btn.frame = CGRectMake(100, 100, 90, 44);
// Set the button's background color.
btn.backgroundColor = [UIColor yellowColor];
// Set the button's normal title.
[btn setTitle:#"Test Button" forState:UIControlStateNormal];
// Add the button as a subview.
[self.view addSubview:btn];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Add this line and try:
btn.layer.cornerRadius = 5;

UIPageControl doesn't properly react to taps

In my TestApp, I created a class "Carousel" which should let me create a swipe menu with a UIPageControl in an easy way (by simply creating an instance of the class Carousel).
Carousel is a subclass of UIView
At init, it creates an UIView, containing UIScrollView, UIPageControl
I can add further UIViews to the scroll view
I don't know if this is the proper way to do it, but my example worked quite well in my TestApp. Swiping between pages works perfectly and the display of the current page in the UIPageControl is correct.
If there were not one single problem: The UIPageControl sometimes reacts to clicks/taps (I only tested in Simulator so far!), sometimes it doesn't. Let's say most of the time it doesn't. I couldn't find out yet when it does, for me it's just random...
As you can see below, I added
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
to my code. I thought this would do the proper handling of taps? But unfortunately, pageChange doesn't get always called (so the value of the UIPageControl doesn't change every time I click).
I would appreciate any input on this because I couldn't find any solution on this yet.
This is what I have so far:
Carousel.h
#import <UIKit/UIKit.h>
#interface Carousel : UIView {
UIScrollView *scrollView;
UIPageControl *pageControl;
BOOL pageControlBeingUsed;
}
- (void)addView:(UIView *)view;
- (void)setTotalPages:(NSUInteger)pages;
- (void)setCurrentPage:(NSUInteger)current;
- (void)createPageControlAt:(CGRect)cg;
- (void)createScrollViewAt:(CGRect)cg;
#end
Carousel.m
#import "Carousel.h"
#implementation Carousel
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Create a scroll view
scrollView = [[UIScrollView alloc] init];
[self addSubview:scrollView];
scrollView.delegate = (id) self;
// Init Page Control
pageControl = [[UIPageControl alloc] init];
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
[self addSubview:pageControl];
}
return self;
}
- (IBAction)pageChange:(id)sender {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageControl.currentPage;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:TRUE];
NSLog(#"%i", pageControl.currentPage);
}
- (void)addView:(UIView *)view {
[scrollView addSubview:view];
}
- (void)createPageControlAt:(CGRect)cg {
pageControl.frame = cg;
}
- (void)setTotalPages:(NSUInteger)pages {
pageControl.numberOfPages = pages;
}
- (void)setCurrentPage:(NSUInteger)current {
pageControl.currentPage = current;
}
- (void)createScrollViewAt:(CGRect)cg {
[scrollView setPagingEnabled:TRUE];
scrollView.frame = cg;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*pageControl.numberOfPages, scrollView.frame.size.height);
[scrollView setShowsHorizontalScrollIndicator:FALSE];
[scrollView setShowsVerticalScrollIndicator:FALSE];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float frac = scrollView.contentOffset.x / scrollView.frame.size.width;
NSUInteger page = lround(frac);
pageControl.currentPage = page;
}
#end
ViewController.m (somewhere in viewDidLoad)
Carousel *carousel = [[Carousel alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
for (int i=0; i<5; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(i * 320, 0, 320, 420)];
UIColor *color;
if(i%3==0) color = [UIColor blueColor];
else if(i%3==1) color = [UIColor redColor];
else color = [UIColor purpleColor];
view.backgroundColor = color;
[carousel addView:view];
view = nil;
}
[carousel setTotalPages:5];
[carousel setCurrentPage:0];
[carousel createPageControlAt:CGRectMake(0,420,320,40)];
[carousel createScrollViewAt:CGRectMake(0,0,320,420)];
Your code is correct. Most likely the frame of your pageControl is pretty small, so theres not a lot of area to look for touch events. You would need to increase the size of the height of pageControl in order to make sure taps are recognized all of the time.

How to hide the keyboard if I have a UIView(contains several textview) in Scrollview

I have several textview in my UIVIew which contained in a scrollview.
First the scollview doesn't not actives when I touched it and want to hide the keyboard after finished the textview editing. So I had build a class name subUIView inherit from scrollview. In that class I overwrite the touch touchbegan events. like the code blow.
But after that the textviewbeingediting function didn't action any more. It's all about touchbegan events calling even when I want to edit the textview.
Could you offer me some advise to solve the problem?Thanks
//
// **subUIView.h**
#import <UIKit/UIKit.h>
#interface subUIView : UIScrollview
{
}
#end
// **subUIView.m**
#import "subUIView.h"
#implementation subUIView
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
self.delegate=self;
//return [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;}
NSInteger a=0;
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event { NSLog(#"touch began ");
// If not dragging, send event to next responder
if (!self.dragging)
{
[self.nextResponder touchesBegan: touches withEvent:event];
}}
in the code blow I alloc scrollview from subUIView and code the textviewbeginediting function.
//
// **ReadExperienceInfoViewController.h**
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#define ExperienceTableName #"pockets"
#class subUIView;
#interface ReadExperienceInfoViewController : UIViewController <UITextFieldDelegate,UITextViewDelegate> {
subUIView *scrollView;
UIView *upView;
UITextField *bookNameTextField;
UITextView *bookExprection;
NSString *getFullDateStr;
BOOL dragging;
}
#property (nonatomic, retain) UIView *upView;
#property (nonatomic, retain) subUIView *scrollView;
//
// **ReadExperienceInfoViewController.m**
//- (void)loadView{
scrollView=[[subUIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
scrollView.contentSize=CGSizeMake(320, 960);
scrollView.backgroundColor=[UIColor colorWithPatternImage :[UIImage imageNamed:#"infomation.png"]];
scrollView.showsVerticalScrollIndicator=YES;
upView=[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
upView=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 960)];
[upView setUserInteractionEnabled:YES];
UILabel *bookName = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 100, 30)];
bookName.backgroundColor = [UIColor clearColor];
bookName.text = #"Title";
bookName.font=[UIFont boldSystemFontOfSize:16];
[upView addSubview:bookName];
[bookName release];
bookNameTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 50, 280, 30)];
bookNameTextField.backgroundColor = [UIColor whiteColor];
bookNameTextField.returnKeyType=UIReturnKeyDone;
bookNameTextField.delegate=self;
[upView addSubview:bookNameTextField];
//lowerView=[[UIView alloc] initWithFrame:CGRectMake(0, 90, 320, 170)];
UILabel *bookExprectionLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 80, 200, 30)];
bookExprectionLabel.text = #"Content";
bookExprectionLabel.font=[UIFont boldSystemFontOfSize:16];
bookExprectionLabel.backgroundColor = [UIColor clearColor];
[upView addSubview:bookExprectionLabel];
[bookExprectionLabel release];
bookExprection = [[UITextView alloc] initWithFrame:CGRectMake(20, 110, 280, 140)];
bookExprection.backgroundColor = [UIColor whiteColor];
bookExprection.font = [UIFont systemFontOfSize:14];
bookExprection.delegate=self;
[upView addSubview:bookExprection];
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Save"
style:UIBarButtonItemStylePlain
target:self action:#selector(updateExperience)]
autorelease];
self.title= bookNameStr;
self.view=scrollView;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
upView.frame=CGRectMake(0, 0, 320, 960);
[UIView commitAnimations];
[bookExprection resignFirstResponder];
[bookNameTextField resignFirstResponder];
}
-(void)textViewDidBeginEditing:(UITextView *)textView{
NSLog(#"texview dig begin editing");
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
if (textView==bookExprection) {
upView.frame=CGRectMake(0, -80, 320, 0);
}
[UIView commitAnimations];
}
Your subUIView.h File must implement the UITextFieldDelegate Protocol, so your viewcontroller responds to the delegate methods for the text field.
Same for the Scroll view, you have to implement the ScrollView
//subUIView.h
#interface subUIView : UIViewController <UIScrollViewDelegate, UITextFieldDelegate>{
//do stuff
}
Then of course you must write scrollView.delegate = self; so that the viewcontroller responds to the delegatemethods for the scroll view.
Hope this helps

UIImagePickerController how to hide the flip camera button?

is there a way to hide the flip camera button inside the UIImagePickerController?
thanks for reading
!^_^!
I ended up using a custom subclass of UIImagePickerController to fix this (and other) issues:
#import "SMImagePickerController.h"
#implementation SMImagePickerController
void hideFlipButtonInSubviews(UIView *view) {
if ([[[view class] description] isEqualToString:#"CAMFlipButton"]) {
[view setHidden:YES];
} else {
for (UIView *subview in [view subviews]) {
hideFlipButtonInSubviews(subview);
}
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
hideFlipButtonInSubviews(self.view);
}
#end
You should be able to create an empty button inside an overlayview that you float on top of the flip camera button. I hacked the code below to test and it seemed to work. Give it a try.
UIView *cameraOverlayView = [[UIView alloc] initWithFrame:CGRectMake(screenSize.width - 100.0f, 5.0f, 100.0f, 35.0f)];
[cameraOverlayView setBackgroundColor:[UIColor blackColor]];
UIButton *emptyBlackButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 35.0f)];
[emptyBlackButton setBackgroundColor:[UIColor blackColor]];
[emptyBlackButton setEnabled:YES];
[cameraOverlayView addSubview:emptyBlackButton];
cameraUI.allowsEditing = YES;
cameraUI.showsCameraControls = YES;
cameraUI.delegate = self;
cameraUI.cameraOverlayView = cameraOverlayView;