I have a question about viewDidLoad in Objc versus Swift. I am not
familiar with Swift, and I'm trying to embed Swift code into our app for the first time.
The swift code I am trying to embed has two classes with inheritance declared this way:
ViewController_A (swift) : ViewController_B (swift) : UIViewController
Both view controllers A and B have "overriding" viewDidLoad methods.
That is they are declared as override func viewDidLoad() {}. If
I leave the above relationship as is and I run with breakpoints set and
print statements in viewDidLoad of both ViewController_A and ViewController_B,
I do see that viewDidLoad for ViewController_A executes before viewDidLoad for
viewController_B. That seems to make perfect sense. Both respective
viewDidLoad methods call super.viewDidLoad. All is good.
I tried to change the inheritance of the above hierarchy by adding an
Objective-C class in the chain:
ViewContoller_A (Swift) : ViewController_B (Swift) : ViewController_C (Objective-C) : UIViewController.
ViewController_C also has a viewDidLoad method which also calls [super viewDidLoad].
If I run with breakpoints set and prints/NSLog at the beginning of all three viewDidLoad methods in all three view controllers, only viewDidLoad for ViewController_C (Objective-C)
executes. The breakpoints/prints in controllers A and B viewDidLoad never appear. I'm not sure this is adding unnecessary info to the topic, but what's interesting is I'll get a warning message:
Attempt to present ViewController_A on ViewController_C whose view is not in the
window hierarchy!
This seems to be telling me that something about ViewController_A is trying to load the view. So just to be sure, as a test, I commented out all the UI related code in ViewContoller_C viewDidLoad. I do not then get that warning message, and it shouldn't prevent ViewController_A viewDidLoad to execute, but the viewDidLoad method in ViewController_A still does not.
Note, ViewController_C is being brought up by a modal storyboard segue, if that adds any relevant info.
So sorry for all that rambling, but is there a way to force the viewDidLoad methods in the Swift subclass view controllers to be executed in the class inheritance hierarchy I outlined above with the Objective-C view controller added to the mix?
Thank you for your help,
Mike
Thank you for your replies. I've added some code below for your review. If I can add anything else, please let me know. Maybe a brief overview will help.
Our App's code set that I've added represents a barcode scanning controller we use to read barcode tags from sample vials. That code is in objc. A customer would like now to read a text code in the same operational context. So in the short term I'm chopping up the barcode scanning controller to get things started.
For this project I brought in a sample app written by Apple available for download from one of their WWDC sessions for reading/recognizing phone numbers. That's the code written in swift. I've made minor modifications to this code so far.
Once the technical hurdles are overcome, I will be re-writing both.
Beginning of BarCodeScan .h and .m files:
#ifndef _BARCODESCANNERCONTROLLER_H_
#define _BARCODESCANNERCONTROLLER_H_
#import <UIKit/UIKit.h>
#import "SystemHeader.h"
#import "RunParameters.h"
#import <RDPDFKit/RDPDFKit.h>
#import "FormInfo.h"
#import "PatientInfo.h"
#import "GradientButton.h"
#import "LogTextInfo.h"
#import "FileIOSupport.h"
#import "PadInfo.h"
#import "XMLiPadAndDBSupport.h"
#import "BarCodeScanSupport.h"
#import "LogonInfo.h"
#import "PadInfo.h" // ocr
#import "TextScanPreviewView.h" // ocr
#define YesIndex 0
//
// With PDF coordintates, pixels are spot on so no shift
// is needed. Keep these at 0 for now.
//
#define SCALE_Y_ADJUSTMENT_FACTOR 0
#define SCALE_X_ADJUSTMENT_FACTOR 0
#define XML_VERSION_STR "<?xml version=\"1.0\"?>"
#define XML_HEADER_NAME "IFD"
#define ResultCount "ResultCount"
#define Result "Result"
#define DoNotCheck #"DoNotCheck"
#define DoNotValidate #"DoNotValidate"
//
// These enums are for determining the action needed when
// an incorrect but valid bar code is scanned.
//
//
// These enums are for determining the action needed when
// an incorrect but valid bar code is scanned.
//
typedef enum IncorrectBarCodeStatusEnum
{
BCNotRecognized = 1,
BCNotUsedButBelongsToAnotherForm,
BCAlreadyUsedByAnotherForm,
BCIsOK
} IncorrectBarCodeStatusEnum;
#interface ReturnResultBarCodeStatus : NSObject
#property (strong, nonatomic) FormInfo * formWhereBarCodeFound;
#property (assign, nonatomic) IncorrectBarCodeStatusEnum incorrectBarCodeStatus;
#end
#interface BarCodeScanController : UIViewController <UIAlertViewDelegate> // Merck2
{
BOOL isScanModeOn;
UIColor *borderColorDefault;
UIColor *viewBackgroundColorDefault;
CGPoint lineStartingPoint;
CGPoint lineEndingPoint;
int barCodeImageViewUpperYCoordinate;
DecoderResult * decoder;
// ocr
NSString * textScanResult;
}
//
// This contains this page's pixel values that are read
// from the xml incoming DBToPad file.
//
#property (strong, nonatomic) Barcode * barCode;
#property (strong, nonatomic) PatientInfo * patient;
#property (strong, nonatomic) FormInfo * form;
#property (strong, atomic) PadInfo * pad;
#property (strong, atomic) LogonInfo * logon;
#property (strong, nonatomic) RDPDFDocument * pdfDocument;
#property (strong, nonatomic) RDPDFDocumentView *pdfDocumentView;
#property (strong, nonatomic) IBOutlet UIImageView * barCodeImageView;
#property (strong, nonatomic) IBOutlet TextScanPreviewView * previewView3;
#property (strong, nonatomic) IBOutlet UIView * cutoutView;
#property (weak, nonatomic) IBOutlet GradientButton *submitButton;
- (IBAction)submitButtonAction:(GradientButton *)sender;
#property (weak, nonatomic) IBOutlet GradientButton *cancelButton;
- (IBAction)cancelButtonAction:(GradientButton *)sender;
#property (weak, nonatomic) IBOutlet GradientButton *scanButton;
- (IBAction)scanButtonAction:(GradientButton *)sender;
#property (weak, nonatomic) IBOutlet UILabel *formLabel;
#property (weak, nonatomic) IBOutlet UILabel *barCodeResultLabel;
#property (strong, nonatomic) BarCodeScanSupport *barCodeScanner;
#property (strong, nonatomic) NSMutableArray *uniqueBarCodes;
- (void) writeBarCodeResultsToXMLDataFile : (BOOL) dataFlag;
- (void) determineIncorrectBarCodeCase
: (NSString *) barCodeResult
: (ReturnResultBarCodeStatus *) returnStatus;
- (void) continueDecodeResultNotification;
#end
#endif
`````` Controller "C"
#import "BarCodeScanController.h"
#implementation ReturnResultBarCodeStatus
#synthesize formWhereBarCodeFound;
#synthesize incorrectBarCodeStatus;
#end
#implementation BarCodeScanController : UIViewController
#synthesize barCode;
#synthesize form;
#synthesize pad;
#synthesize logon;
#synthesize submitButton;
#synthesize cancelButton;
#synthesize scanButton;
#synthesize barCodeImageView;
#synthesize previewView3;
#synthesize cutoutView;
#synthesize pdfDocument;
#synthesize pdfDocumentView;
#synthesize barCodeScanner;
#synthesize uniqueBarCodes;
#synthesize barCodeResultLabel;
#synthesize formLabel;
//
// This is temporary for the demo. Should read whats in the file
// if we scanned on a page, srolled to another, and came back to
// the scanned page. But for now, just .
//
static NSString * saveBarCodeResult;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[NSThread sleepForTimeInterval : 2.0];
[super viewDidLoad];
if (deviceIsIPad)
{
self.formLabel.text = [NSString stringWithFormat:
#"%#",form.form];
}
else
{
self.formLabel.text = [NSString stringWithFormat:
#" %#",form.form];
}
[self.cancelButton useAlertStyle];
[self.submitButton useAlertStyle];
[self.scanButton useAlertStyle];
if (self.form.displaySubmitButton == NO)
{
self.submitButton.hidden = YES;
}
else
{
self.submitButton.hidden = NO;
}
//
// This creates an .ifd file with no data in it. The data base server requires
// a file with valid data or null data for each vas page. So if the user
// evenutally enters valid data, it will overwrite this. Creating the file here
// is completely dependent on the user having to scroll to the end of the form
// to do a submit. In doing so, every vas page must be passed along the way
// which will initiate this call. If we ever go back to starting out with a
// submit button on every page when the form is brought up, then we will have
// to create a null .ifd file in the forms modal controller, which will mean
// we will have to have duplicate functionality in both that controller and
// this one. Note, later added code made sure file does not first exist. It
// may already be there if there was a crash during the submit. In this case
// don't write a null file, and set vsLineDrawn to YES.
//
//
FileIOSupport * sandbox = [[FileIOSupport alloc] init];
if (![sandbox checkIfFileExistsAtPath : self.barCode.bcPngPathNameData])
{
[self writeBarCodeResultsToXMLDataFile : NO];
}
[[NSNotificationCenter defaultCenter] addObserver: self selector:#selector(decodeResultNotification:) name: BarCodeResultNotification object: nil];
// ocr x
// Comment out for text reader.
// barCodeScanner = [[BarCodeScanSupport alloc] init : barCodeImageView : self
// : self.barCode.bcFormat];
// ocr x
// Comment out for text reader
// self.barCodeImageView.image = [UIImage imageWithContentsOfFile :
// barCode.bcPngPathNameImage];
[scanButton setTitle:Retry_Str forState:UIControlStateNormal];
//
// Setting isScanModeOn works for both cases below. If we have a prior
// result we don't start the scanner but put up the previous result and
// image. If there is no prior result, we call the scanButtonAction code
// to start the scanner.
//
isScanModeOn = NO;
if (self.barCode.bcImageBuffer != nil)
{
self.barCodeResultLabel.hidden = NO;
self.barCodeResultLabel.text = barCode.bcSelectedResult;
self.barCodeResultLabel.backgroundColor = [UIColor yellowColor];
}
else
{
self.barCodeResultLabel.hidden = YES;
self.barCodeResultLabel.text = Blank;
//
// Start scanner
//
[self scanButtonAction : scanButton];
}
// ocr
// This normally gets allocatd in bar code scanner support which we are not
// using for ocr. So allocate it here. We're only using the text part of it
// so as to not disturb alot of code.
//
decoder = [DecoderResult alloc];
// ocr
// Comment out for text reader
// barCodeImageViewUpperYCoordinate = self.barCodeImageView.frame.origin.y;
DISPATCH_TO_MAIN_THREAD
// ocr x
// Comment out for text reader
// [self startScanning];
// [self performSegueWithIdentifier:TextReaderModalSegue sender:self];
DISPATCH_OUT_OF_MAIN_THREAD
}
.
.
.
Contents of Project-Bridging-Header.h
// ocr
#import "ObjectiveCTestWithSwift.h"
#import "BarCodeScanController.h"
#import "TextScanPreviewView.h"
One of Apple's controllers - Controller "B". Note I tried viewDidLoad
with "public" keyword and without.
import UIKit
import AVFoundation
import Vision
// ocr
// class TextReaderViewController: UIViewController {
class TextReaderViewController : BarCodeScanController {
// MARK: - UI objects
//
// ocr
// Comment these out. These outlets will be provided by barcodescancontroller.
// #IBOutlet weak var cutoutView: UIView!
// #IBOutlet weak var previewView: PreviewView!
#IBOutlet weak var numberView: UILabel!
var maskLayer = CAShapeLayer()
// Device orientation. Updated whenever the orientation changes to a
// different supported orientation.
var currentOrientation = UIDeviceOrientation.portrait
// MARK: - Capture related objects
private let captureSession = AVCaptureSession()
let captureSessionQueue = DispatchQueue(label: "com.example.apple-samplecode.CaptureSessionQueue")
var captureDevice: AVCaptureDevice?
var videoDataOutput = AVCaptureVideoDataOutput()
let videoDataOutputQueue = DispatchQueue(label: "com.example.apple-samplecode.VideoDataOutputQueue")
// MARK: - Region of interest (ROI) and text orientation
// Region of video data output buffer that recognition should be run on.
// Gets recalculated once the bounds of the preview layer are known.
var regionOfInterest = CGRect(x: 0, y: 0, width: 1, height: 1)
// Orientation of text to search for in the region of interest.
var textOrientation = CGImagePropertyOrientation.up
// MARK: - Coordinate transforms
var bufferAspectRatio: Double!
// Transform from UI orientation to buffer orientation.
var uiRotationTransform = CGAffineTransform.identity
// Transform bottom-left coordinates to top-left.
var bottomToTopTransform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -1)
// Transform coordinates in ROI to global coordinates (still normalized).
var roiToGlobalTransform = CGAffineTransform.identity
// Vision -> AVF coordinate transform.
var visionToAVFTransform = CGAffineTransform.identity
// MARK: - View controller methods
// ocr
public override func viewDidLoad() {
// ocr
print("viewDidLoad TextReaderViewController")
// ocr x
super.viewDidLoad()
// Set up preview view.
previewView3.session = captureSession
// Set up cutout view.
cutoutView.backgroundColor = UIColor.gray.withAlphaComponent(0.5)
maskLayer.backgroundColor = UIColor.clear.cgColor
maskLayer.fillRule = .evenOdd
cutoutView.layer.mask = maskLayer
// Starting the capture session is a blocking call. Perform setup using
// a dedicated serial dispatch queue to prevent blocking the main thread.
captureSessionQueue.async {
self.setupCamera()
// Calculate region of interest now that the camera is setup.
DispatchQueue.main.async {
// Figure out initial ROI.
self.calculateRegionOfInterest()
}
}
// ocr
// super.viewDidLoad()
}
.
.
.
2nd of Apples Controller "A". Note I tried viewDidLoad
with "public" keyword and without.
import Foundation
import UIKit
import AVFoundation
import Vision
// ocr
// class VisionViewController: ViewController {
class VisionViewController: TextReaderViewController {
var request: VNRecognizeTextRequest!
// Temporal string tracker
let numberTracker = StringTracker()
public override func viewDidLoad() {
// Set up vision request before letting ViewController set up the camera
// so that it exists when the first buffer is received.
// ocr
print("viewDidLoad VisionViewController")
request = VNRecognizeTextRequest(completionHandler: recognizeTextHandler)
super.viewDidLoad()
}
.
.
.
in
#interface BarCodeScanController : UIViewController
// place a definition, so swift (Xcode) can see it.
-(void)viewDidLoad;
#end
I discovered my perform segue was executing the wrong view controller. Once I corrected that, the viewDidLoad's executed in the correct sequence.
Related
I am trying to update the contents of an NSTextView that is connected to myViewController as a referencing outlet to the Files Owner which is the subclass myViewController.
When I use an IBAction from a button, or use the viewDidLoad method of the controller, I can update the text fine. However, when I try run the method from another class (referred to in this example as anotherViewController), it runs the method, but the textview does not change.
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
self.outText.string = #"I am updated text! I also work!";
}
- (void)updateTextView:(NSString *)argText {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
- (void)updateTextViewWithoutArg {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
#end
In anotherViewController.m , which has all the relevant imports, I call this:
myViewController *viewtask = [[myViewController alloc] init];
[viewtask updateTextViewWithoutArg];
Nothing happens. The method runs and logs that it should have updated, but no text updates. I have tried many different approaches, including textstorage and scrollrange methods, they all work the already working sections, but make no difference in the sections not working.
I've also tried just for fun:
myViewController *viewtask;
[viewtask updateTextViewWithoutArg];
Also using the instance variable _outText
Also using [self.outText setString:#"string"];
Also using [_outText setString:#"string"];
Again, they work but only in the already working sections.
This should be simple but isn't logical to me. In swift all I need to do is
self.outText.string = "I update whenever I'm called!"
Views you create in Interface Builder are lazily created, so if you access them before viewDidLoad is called they are nil.
If your case, calling
myViewController *viewtask = [[myViewController alloc] init];
does not cause the views to be created so when you call
[viewtask updateTextViewWithoutArg];
self.outText is nil.
You can see that this is what is happening by updating your code as below:
- (void)updateTextView:(NSString *)argText {
NSAssert(self.outText != nil, #"self.outText must not be nil");
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
you should see the assert fire.
I appear to have found a solution by making myViewController a singleton class and using sharedInstance. For this particlar app, myViewController is a debug output window and will never need to be placed in another view.
I won't accept this answer yet, as it's not the best one I'm sure. There may still be a proper solution presented that allows finding the applicable myViewController instance, and modifying the outText property attached to it. Using this singleton makes subclassing tedious as I would have to make a new class for every instance if I wanted to be able to address say 10 View Controllers.
Anyway - the way I've been able to satisfy my simple requirement:
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
+ (id)sharedInstance;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
static myViewController *sharedInstance = nil;
+ (myViewController *)sharedInstance {
static myViewController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[myViewController alloc] init];
});
return sharedInstance;
}
- (void)viewDidLoad {
[super viewDidLoad];
sharedInstance = self;
}
- (void)viewDidUnload {
sharedInstance = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
sharedInstance.outText.string = #"Button Pressed";
}
- (void)updateTextView:(NSString *)argText {
sharedInstance.outText.string = argText;
}
- (void)updateTextViewWithoutArg {
sharedInstance.outText.string = #"I make it to the TextView now";
}
#end
Now when I use this code from within anotherViewController.m it updates the right instance:
[myViewController.sharedInstance updateTextView:#"Updating with this string"];
I know this has been posted many times but I cannot get it to work, there are no errors in my code (found by Xcode), but what I'm trying to do isn't working.
1- Setup: I have 2 classes, ViewController (UIViewController) and GraphView (UIView)
2- What I am trying to do: I have a BOOL type variable plotPressedVC in ViewController that takes an initial value NO.
Once a button is pressed (this IBAction is located in the ViewController.m file) plotPressedVC = YES. From this, in my GraphView.m file I have an if statement whose condition is met if pressed = YES.
3- My code:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
#public BOOL plotPressedVC;
}
- (IBAction)plot:(id)sender;
#end
ViewController.h
#import "ViewController.h"
#import "GraphView.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
plotPressedVC = YES;
}
- (IBAction)plot:(id)sender {
plotPressedVC = YES;
GraphView *plotPress = [[GraphView alloc] init];
plotPress.plotPressed = plotPressedVC;
}
#end
GraphView.h
#import <UIKit/UIKit.h>
#interface GraphView : UIView {
BOOL plotPressed;
}
#property (nonatomic) BOOL plotPressed;
#end
GraphView.m
#import "GraphView.h"
#import "ViewController.h"
#implementation GraphView
- (void)drawLineGraphWithContext:(CGContextRef)ctx {
if (plotPressed == YES) {
NSLog(#"yep");
}
// Other code that I want to be in if statement
}
#end
4- Question: What have I done wrong for the if statement's condition to not have been met when pushing the button plot?
EDIT: added NSlog at the end of the plot action, I am getting values 80, -64, -128...
- (IBAction)plot:(id)sender {
plotPressedVC = YES;
NSLog(#"%hhd",plotPressedVC);
GraphView *plotPress = [[GraphView alloc] init];
plotPress.plotPressed = plotPressedVC;
NSLog(#"%hhd",plotPress);
}
New Question: How come plotPress is receiving other numbers than 0 or 1?
It's been asked many times and it usually has the same answer.
The problem is here:
GraphView *plotPress = [[GraphView alloc] init];
plotPress.plotPressed = plotPressedVC;
Here you create a new graph view, set the value on it, and then do nothing else with it. You need to have an outlet or property to the existing graph view and set the plotPressed value on that.
Currently that graph view is never added to another view, and under ARC will be immediately deallocated at the end of the method.
I have controller
#import <UIKit/UIKit.h>
#import "ViewBoard.h"
#interface BallsViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *InfoLabel;
#property (weak, nonatomic) IBOutlet UIButton *nextBallButton;
#property (weak, nonatomic) IBOutlet UILabel *PointLabel;
#property (weak, nonatomic) IBOutlet ViewBoard *viewBoard;
- (IBAction)NewGame:(id)sender;
#end
#import "BallsViewController.h"
#import "Field.h"
#interface BallsViewController ()
#end
#implementation BallsViewController
#synthesize viewBoard;
- (void)viewDidLoad
{
[super viewDidLoad];
[self.viewBoard Draw:#"Fields"];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)NewGame:(id)sender {
self.viewBoard.canDrawBall = true;
[self.viewBoard Draw:#"Fields"];
}
#end
And UIView
#interface ViewBoard : UIView
#end
#implementation ViewBoard
-(void)sendScoreToUI{
int score = 10;
}
#end
How can I send information about score to UI and there set it to label ? I want to UIView send this information to controller than controller get it from UIView.
Consider MVC, Model - View - Controller.
The View is ViewBoard.
The Controller is BallsViewController, where the app logic is contained.
The Model should be the score.
So you have 3 choices on how to manage Model. Note that in my case the app logic is always inside the controller so is the controller that manages the game and the score, and not the UI.
Choice-1 : strict MVC
the score is modelled as an independent object. In such case you define a "Score" class, you send score updates from the controller to the model and let the view to listen for model changes:
#interface Score
#property (nonatomic,assign) NSInteger points;
#end
#implementation Score
#synthesize points;
#end
Then the controller instantiates the object score:
Score *myScore;
updates it when an scoring event happens:
[myScore setPoints:30];
Finally you can use KVO to let the ViewBoard to listen for changes of "points" property on myScore. So inside the controller, after myScore is initialised:
[myScore addObserver:self.viewBoard forKeyPath:#"points" options:NSKeyValueOptionNew context:NULL];
Note: the model and the view are linked by KVO only. So the view doesn't change the score, and the model notifies view only thanks to the KVO process. When the controller disappears, the KVO link is broken.
Choice-2 : the model is inside the controller
In such case you just add a new property to your controller:
#property (nonatomic,assign) NSInteger points;
and every time you update the score you send the new value to the view (which updates itself). You can do this in the points setter: each time you update your internal points property you also ask viewBoard to update itself.
[self setPoints:30];
-(void)setPoints:(NSInteger)newPoints {
points = newPoints;
[self.viewBoard updatePoints:points];
}
Choice-3 : the model is inside the view
This approach is simple but usually not recommended, because normally you don't want to add a strong dependency between the controller and the view representation (this happens because your view requirements could affect the way the view controller updates its logic). Also a limitation is that in a view unload event you could lose the score.
In such case you add the points property to the view:
#property (nonatomic,assign) NSInteger points;
and in your view controller you can change the points in this way:
[self.viewBoards setPoints:30];
finally your view "setPoints:" setter will contain some "refresh" logic:
-(void)setPoints:(NSInteger)newPoints {
points = newPoints;
[self setNeedsLayout];
}
-(void)layoutSubviews {
// here you update the subview
[self.pointsLabel setText:[NSString stringWithFormat:#"%d",self.points]];
}
I have a UITextField set up in Interface Builder with a background image. The background shows up fine, but when I switch (in IB) the Class name to my UITextField subclass (ValidatedTextField), the background image doesn't show. Can anyone spot any reason why the image should not be there for my UITextView subclass?
Other info
Don't know if this helps but IB has also been giving me some trouble -- sometimes not allowing me to change the class name of these text fields..
//ValidatedTextField.h
#import <UIKit/UIKit.h>
#import "MBValidated.h"
#interface ValidatedTextField : UITextView <MBValidated>
// the maximum characters allowed
#property (assign, nonatomic) int mbMaxLength;
// an visual indicator of the validation state (checkmark, etc)
#property (strong, nonatomic) UIImageView *mbStatusImageView;
// whether the field can be empty
#property (assign, nonatomic) BOOL mbIsRequired;
// whether we have succesfully validated
#property (assign, nonatomic) BOOL mbIsValid;
// validate and update stored validated state
-(BOOL)mbValidate;
#end
// ValidatedTextField.m
#import "ValidatedTextField.h"
#implementation ValidatedTextField
#synthesize mbMaxLength, mbStatusImageView;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// set any default values here
-(void)mbSetDefaults
{
self.mbIsRequired = YES;
self.mbIsValid = YES;
}
-(BOOL)mbValidate
{
// validate length
if(self.text.length > self.mbMaxLength) self.mbIsValid = NO;
// validate empty or filled
if(self.text.length == 0 && self.mbIsRequired == YES) self.mbIsValid = NO;
return self.mbIsValid;
}
- (void)awakeFromNib
{
// set defaults
[self mbSetDefaults];
}
#end
// MBValidated Protocol
#import <Foundation/Foundation.h>
#protocol MBValidated <NSObject>
// whether the field can be empty
#property (assign, nonatomic) BOOL mbIsRequired;
// whether we have succesfully validated
#property (assign, nonatomic) BOOL mbIsValid;
// validate the item
-(BOOL)mbValidate;
#end
You should subclass UITextField and not UITextView.
I have put together a simple quote generator, storing the quotes in an array. Following are the quote view controller interface and implementation files:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property(nonatomic, retain)NSArray *myQuotes;
#property(nonatomic, retain)NSMutableArray *movieQuotes;
#property (nonatomic, retain) IBOutlet UITextView *quote_text;
-(IBAction)quote_btn_touch:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myQuotes;
#synthesize movieQuotes;
#synthesize quote_text;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.myQuotes = [NSArray arrayWithObjects:
#"Live and let live",
#"Don't cry over spilt milk",
#"Always look on the bright side of life",
#"Nobody's perfect",
#"Can't see the woods for the trees",
#"Better to have loved and lost than not loved at all",
#"The early bird catches the worm",
#"As slow as a wet week",
nil];
quote_text = nil;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
-(IBAction)quote_btn_touch:(id)sender {
// 1 - Get number of rows in array
int array_tot = [self.myQuotes count];
// 2 - Get random index
int index = (arc4random() % array_tot);
// 3 - Get the quote string for the index
NSString *my_quote = [self.myQuotes objectAtIndex:index];
// 4 - Display the quote in the text view
self.quote_text.text = [NSString stringWithFormat:#"Quote:\n\n%#", my_quote];
}
#end
In the xib file, I connected the text view and button to the File's Owner, using quote_text and quote_btn_touch respectively.
Trouble is that when I click on the button, nothing happens. Any idea what I missed?
Thanks in advance!
your setting quote text to nil on viewDidLoad. If you get rid of that your code should work as long as you correctly bound the button to the viewcontroller function
quote_text = nil;