From 9da395527b53214be07c2d3a12da84cd12c2db74 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 22 Apr 2026 21:59:20 +0200 Subject: [PATCH 01/33] Clean up .gitignore and untrack xcuserdata --- .gitignore | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.gitignore b/.gitignore index e43b0f9..1f14db7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,25 @@ +# macOS .DS_Store + +# Xcode user-specific files +xcuserdata/ +*.xcuserstate +*.xcscmblueprint + +# Build products +build/ +DerivedData/ + +# Swift Package Manager +.swiftpm/ +.build/ +Package.resolved + +# Build artifacts +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +# CocoaPods (falls später benötigt) +Pods/ From 052e7fc5ab4cf941cff2e2d23eba78be77cff8e7 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 1 May 2026 17:23:23 +0200 Subject: [PATCH 02/33] Enable ARC and exclude CocosDenshion from ARC --- LabSolitaire.xcodeproj/project.pbxproj | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/LabSolitaire.xcodeproj/project.pbxproj b/LabSolitaire.xcodeproj/project.pbxproj index 57d99ca..d985466 100755 --- a/LabSolitaire.xcodeproj/project.pbxproj +++ b/LabSolitaire.xcodeproj/project.pbxproj @@ -101,10 +101,10 @@ 26AE5BF6144292AF00570ABC /* UnderlineNarrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 26AE5BF5144292AF00570ABC /* UnderlineNarrow.png */; }; 26B1E3E3131D608900DFA4EA /* iTunesArtwork in Resources */ = {isa = PBXBuildFile; fileRef = 26B1E3E1131D608900DFA4EA /* iTunesArtwork */; }; 26B1E41D131D6E6900DFA4EA /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 26B1E41C131D6E6900DFA4EA /* Icon-72.png */; }; - 26B3E29C148404EC00C3459E /* CDAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E294148404EC00C3459E /* CDAudioManager.m */; }; - 26B3E29D148404EC00C3459E /* CDOpenALSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E297148404EC00C3459E /* CDOpenALSupport.m */; }; - 26B3E29E148404EC00C3459E /* CocosDenshion.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E299148404EC00C3459E /* CocosDenshion.m */; }; - 26B3E29F148404EC00C3459E /* SimpleAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E29B148404EC00C3459E /* SimpleAudioEngine.m */; }; + 26B3E29C148404EC00C3459E /* CDAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E294148404EC00C3459E /* CDAudioManager.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 26B3E29D148404EC00C3459E /* CDOpenALSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E297148404EC00C3459E /* CDOpenALSupport.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 26B3E29E148404EC00C3459E /* CocosDenshion.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E299148404EC00C3459E /* CocosDenshion.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 26B3E29F148404EC00C3459E /* SimpleAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E29B148404EC00C3459E /* SimpleAudioEngine.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 26B3E2A91484084700C3459E /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B3E2A81484084700C3459E /* OpenAL.framework */; }; 26B3E2AB1484086600C3459E /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B3E2AA1484086600C3459E /* AudioToolbox.framework */; }; 26B6709914EF69FA00C8E6B2 /* 1C@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 26B6708914EF69FA00C8E6B2 /* 1C@2x.png */; }; @@ -316,7 +316,7 @@ 26AE5BE21442601C00570ABC /* Highlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Highlight.png; sourceTree = ""; }; 26AE5BF31442926100570ABC /* UnderlineWide.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = UnderlineWide.png; sourceTree = ""; }; 26AE5BF5144292AF00570ABC /* UnderlineNarrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = UnderlineNarrow.png; sourceTree = ""; }; - 26B1E3E1131D608900DFA4EA /* iTunesArtwork */ = {isa = PBXFileReference; lastKnownFileType = file; path = iTunesArtwork; sourceTree = ""; }; + 26B1E3E1131D608900DFA4EA /* iTunesArtwork */ = {isa = PBXFileReference; lastKnownFileType = text; path = iTunesArtwork; sourceTree = ""; }; 26B1E41C131D6E6900DFA4EA /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-72.png"; sourceTree = ""; }; 26B3E293148404EC00C3459E /* CDAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDAudioManager.h; sourceTree = ""; }; 26B3E294148404EC00C3459E /* CDAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDAudioManager.m; sourceTree = ""; }; @@ -1097,6 +1097,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; @@ -1105,7 +1106,8 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = LabSolitaire_Prefix.pch; INFOPLIST_FILE = "LabSolitaire-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.card-games"; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; PRODUCT_BUNDLE_IDENTIFIER = com.softdorothy.labsolitaire; PRODUCT_NAME = "Lab Solitaire"; PROVISIONING_PROFILE = ""; @@ -1116,13 +1118,15 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = LabSolitaire_Prefix.pch; INFOPLIST_FILE = "LabSolitaire-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.card-games"; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; PRODUCT_BUNDLE_IDENTIFIER = com.softdorothy.labsolitaire; PRODUCT_NAME = "Lab Solitaire"; PROVISIONING_PROFILE = ""; From 1582f9d6a9f3655d3a250f1e7d973cf91d666cb5 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2026 12:37:00 +0200 Subject: [PATCH 03/33] Migrate CardEngine, Classes, and main.m to ARC - Remove retain/release/autorelease calls - Replace NSAutoreleasePool with @autoreleasepool in main.m - Convert @property (retain) to @property (strong) - Remove dealloc bodies that only contained release calls CocosDenshion files excluded via -fno-objc-arc compiler flag. --- CardEngine/CECard.m | 8 -------- CardEngine/CECardDealer.h | 6 +++--- CardEngine/CECardDealer.m | 23 ++++++++--------------- CardEngine/CECardView.h | 6 +++--- CardEngine/CECardView.m | 25 +++++-------------------- CardEngine/CEStack.m | 31 +++++++------------------------ CardEngine/CEStackView.h | 20 ++++++++++---------- CardEngine/CETableView.h | 4 ++-- CardEngine/CETableView.m | 13 ------------- Classes/LabSolitaireAppDelegate.h | 4 ++-- Classes/LabSolitaireAppDelegate.m | 9 --------- Classes/LocalPlayer.h | 2 +- Classes/LocalPlayer.m | 26 +++----------------------- main.m | 14 ++++++-------- 14 files changed, 50 insertions(+), 141 deletions(-) diff --git a/CardEngine/CECard.m b/CardEngine/CECard.m index 44232cf..7fae369 100644 --- a/CardEngine/CECard.m +++ b/CardEngine/CECard.m @@ -203,14 +203,6 @@ - (id) initWithIndex: (NSUInteger) index return myself; } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - // Super. - [super dealloc]; -} - // ---------------------------------------------------------------------------------------------------------------- suit - (CESuit) suit diff --git a/CardEngine/CECardDealer.h b/CardEngine/CECardDealer.h index c78a0e2..2f7ea0a 100644 --- a/CardEngine/CECardDealer.h +++ b/CardEngine/CECardDealer.h @@ -25,12 +25,12 @@ id _delegate; } -@property(nonatomic,retain,readonly) CEStackView *sourceStack; -@property(nonatomic,retain,readonly) CEStackView *destStack; +@property(nonatomic,strong,readonly) CEStackView *sourceStack; +@property(nonatomic,strong,readonly) CEStackView *destStack; @property(nonatomic) NSTimeInterval dealDuration; // Default: 0.25 seconds. @property(nonatomic) NSTimeInterval dealDelay; // Default: 0.20 seconds. @property(nonatomic) BOOL enableUndoGrouping; -@property(nonatomic,assign) id delegate; // Optional delegate. +@property(nonatomic,weak) id delegate; // Optional delegate. @property(nonatomic,readonly) BOOL dealing; - (void) dealCardsFromStackView: (CEStackView *) source toStackView: (CEStackView *) dest count: (NSUInteger) count; diff --git a/CardEngine/CECardDealer.m b/CardEngine/CECardDealer.m index 080b8f7..45a4402 100644 --- a/CardEngine/CECardDealer.m +++ b/CardEngine/CECardDealer.m @@ -54,17 +54,10 @@ - (void) dealloc // Finish any deal in progress. if (_dealing) [self quickComplete]; - - // Release instance var. - [_sourceStack release]; - [_destStack release]; - + // Clean up timer. if (_dealTimer) [_dealTimer invalidate]; - - // Super. - [super dealloc]; } // --------------------------------------------------------------------------------------------------------- dealOneCard @@ -80,8 +73,8 @@ - (void) dealOneCard if (_enableUndoGrouping) [[CETableView sharedCardUndoManager] endUndoGrouping]; _count = 0; - [_sourceStack release]; - [_destStack release]; + _sourceStack = nil; + _destStack = nil; _dealing = NO; } else @@ -118,9 +111,9 @@ - (void) dealCardsFromStackView: (CEStackView *) source toStackView: (CEStackVie if ([source.stack topCard] == nil) return; - // Retain stacks. - _sourceStack = [source retain]; - _destStack = [dest retain]; + // Store stacks. + _sourceStack = source; + _destStack = dest; _count = count; _dealing = YES; @@ -169,8 +162,8 @@ - (void) quickComplete if (_enableUndoGrouping) [[CETableView sharedCardUndoManager] endUndoGrouping]; _count = 0; - [_sourceStack release]; - [_destStack release]; + _sourceStack = nil; + _destStack = nil; _dealing = NO; } } diff --git a/CardEngine/CECardView.h b/CardEngine/CECardView.h index 91ef822..82b87c4 100644 --- a/CardEngine/CECardView.h +++ b/CardEngine/CECardView.h @@ -30,9 +30,9 @@ enum BOOL _hasShadow; // Shadow state. } -@property(nonatomic,retain) CECard *card; // The card object backing the view. This is the card displayed. -@property(nonatomic,retain) UIColor *highlightColor; // Fill color when view is highlighted. Can be set to nil. -@property(nonatomic,retain) NSString *label; // Fill color when view is highlighted. Can be set to nil. +@property(nonatomic,strong) CECard *card; // The card object backing the view. This is the card displayed. +@property(nonatomic,strong) UIColor *highlightColor; // Fill color when view is highlighted. Can be set to nil. +@property(nonatomic,strong) NSString *label; // Fill color when view is highlighted. Can be set to nil. @property(nonatomic) BOOL highlight; // During live drag, view may highlight when stack dragged into. @property(nonatomic) BOOL hasShadow; // Whether to draw a shadow beneath card. Default is NO. diff --git a/CardEngine/CECardView.m b/CardEngine/CECardView.m index 9fbc721..27de256 100644 --- a/CardEngine/CECardView.m +++ b/CardEngine/CECardView.m @@ -104,18 +104,6 @@ - (id) initWithFrame: (CGRect) frame return self; } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - // Release instance var. - [_card release]; - [_highlightColor release]; - - // Super. - [super dealloc]; -} - // ------------------------------------------------------------------------------------------------------------ isOpaque - (BOOL) isOpaque @@ -132,9 +120,8 @@ - (void) setCard: (CECard *) card if (_card == card) return; - // Release, retain, redraw. - [_card release]; - _card = [card retain]; + // Assign, redraw. + _card = card; [self setNeedsDisplay]; } @@ -155,9 +142,8 @@ - (void) setLabel: (NSString *) label if (label == _label) return; - // Release, retain, assign. - [_label release]; - _label = [label retain]; + // Assign. + _label = label; // Redraw. [self setNeedsDisplay]; @@ -272,9 +258,8 @@ - (void) drawRect: (CGRect) rect NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; style.lineBreakMode = NSLineBreakByWordWrapping; style.alignment = NSTextAlignmentCenter; - [_label drawInRect: box withAttributes: [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, + [_label drawInRect: box withAttributes: [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, style, NSParagraphStyleAttributeName, nil]]; - [style release]; } } diff --git a/CardEngine/CEStack.m b/CardEngine/CEStack.m index dbd0aa0..d604c1a 100644 --- a/CardEngine/CEStack.m +++ b/CardEngine/CEStack.m @@ -17,7 +17,7 @@ @interface CardData : NSObject CECard *_card; BOOL _promised; } -@property(nonatomic,retain) CECard *card; +@property(nonatomic,strong) CECard *card; @property(nonatomic,getter=isPromised) BOOL promised; @end @@ -37,7 +37,7 @@ + (id) deckOfCards NSUInteger i; id deck; - deck = [[[CEStack alloc] init] autorelease]; + deck = [[CEStack alloc] init]; require (deck, bail); // Add deck of cards. @@ -48,7 +48,6 @@ + (id) deckOfCards // Add card. card = [[CECard alloc] initWithIndex: i]; [deck addCardWithoutNotification: card]; - [card release]; } bail: @@ -74,17 +73,6 @@ - (id) init return myself; } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - // Release instance variables. - [_cards release]; - - // Super. - [super dealloc]; -} - #pragma mark ------ card accessors // ------------------------------------------------------------------------------------------------------- numberOfCards @@ -199,7 +187,7 @@ - (NSArray *) cardArrayRepresentation NSUInteger count, i; // Create empty array. - array = [[[NSMutableArray alloc] initWithCapacity: 3] autorelease]; + array = [[NSMutableArray alloc] initWithCapacity: 3]; // For each card, add an NSNumber representing the value of the card to array. count = [self numberOfCards]; @@ -316,7 +304,6 @@ - (void) insertCard: (CECard *) card atIndex: (NSUInteger) index cardData = [[CardData alloc] init]; [cardData setCard: card]; [_cards insertObject: cardData atIndex: index]; - [cardData release]; // Notify observers that the card stack was changed. [[NSNotificationCenter defaultCenter] postNotificationName: @"StackDidChangeCount" object: self userInfo: nil]; @@ -342,7 +329,6 @@ - (void) addDeckWithRandomTransform: (BOOL) random if (random) [card randomizeTransform]; [self addCardWithoutNotification: card]; - [card release]; } // Notify observers that the card stack was changed. @@ -379,14 +365,13 @@ - (void) addCardsFromCardArrayRepresentation: (NSArray *) array randomize: (BOOL card = [[CECard alloc] initWithIndex: cardIndex & kCardIndexMask]; if ((cardIndex & kCardIsFaceUpFlag) == kCardIsFaceUpFlag) [card setFaceUp: YES]; - + // BUG: Randomizing the transform needs to be done *before* the card is added to the stack for some reason. if (randomize) [card randomizeTransform]; - + // Add card. [self addCardWithoutNotification: card]; - [card release]; } // Notify observers that the card stack was changed. @@ -678,10 +663,9 @@ - (void) addCardWithoutNotification: (CECard *) card cardData = [[CardData alloc] init]; [cardData setCard: card]; [_cards addObject: cardData]; - [cardData release]; - + bail: - + return; } @@ -703,7 +687,6 @@ - (void) promiseCard: (CECard *) card [cardData setCard: card]; [cardData setPromised: YES]; [_cards addObject: cardData]; - [cardData release]; _cardPromised = YES; diff --git a/CardEngine/CEStackView.h b/CardEngine/CEStackView.h index 0455504..088b4e2 100644 --- a/CardEngine/CEStackView.h +++ b/CardEngine/CEStackView.h @@ -81,23 +81,23 @@ enum BOOL _enableUndoGrouping; } -@property(nonatomic,retain) CEStack *stack; // The card stack associated with the view. +@property(nonatomic,strong) CEStack *stack; // The card stack associated with the view. @property(nonatomic) CEStackViewLayout layout; // This property determines the layout of the cards displayed. @property(nonatomic) CECardSize cardSize; // Specifies the size of PlayingCardView created. @property(nonatomic) CEStackDragPermissions dragPermissions; // Indicates if cards can be dragged to or from stack. @property(nonatomic) BOOL allowsReordering; // Enables reordering of cards by user. Default = NO. -@property(nonatomic,retain) UIColor *borderColor; // Can be nil to turn off border. -@property(nonatomic,retain) UIColor *fillColor; // Can be nil to turn off rounded-rect fill. -@property(nonatomic,retain) UIColor *highlightColor; // Can be nil to turn off highlighting (not recommended). -@property(nonatomic,retain) NSString *label; // Can be nil to turn off label. -@property(nonatomic,retain) UIFont *labelFont; // Can be nil to turn off label. -@property(nonatomic,retain) UIColor *labelColor; // Can be nil to turn off label. +@property(nonatomic,strong) UIColor *borderColor; // Can be nil to turn off border. +@property(nonatomic,strong) UIColor *fillColor; // Can be nil to turn off rounded-rect fill. +@property(nonatomic,strong) UIColor *highlightColor; // Can be nil to turn off highlighting (not recommended). +@property(nonatomic,strong) NSString *label; // Can be nil to turn off label. +@property(nonatomic,strong) UIFont *labelFont; // Can be nil to turn off label. +@property(nonatomic,strong) UIColor *labelColor; // Can be nil to turn off label. @property(nonatomic) CGFloat cornerRadius; // Set to -1 for automatic. Default is automatic. @property(nonatomic) BOOL displaysCount; // Display count of cards (only for kCEStackViewLayoutStacked). Default = NO. @property(nonatomic) BOOL highlight; // During a live drag, view highlights when dragged into and allowed. -@property(nonatomic,assign) id delegate; // Optional delegate for the view. -@property(nonatomic,retain) NSString *identifier; // An identifier you can choose to associate a StackView with. -@property(nonatomic,retain) NSString *archiveIdentifier; // An identifier used to archive the state of a stack. Default is nil. +@property(nonatomic,weak) id delegate; // Optional delegate for the view. +@property(nonatomic,strong) NSString *identifier; // An identifier you can choose to associate a StackView with. +@property(nonatomic,strong) NSString *archiveIdentifier; // An identifier used to archive the state of a stack. Default is nil. @property(nonatomic,getter=isOrderly) BOOL orderly; // If not orderly cards may have a random rotation or offset applied. @property(nonatomic) BOOL enableUndoGrouping; // Whether to wrap moving multiple cards in an undo group. Default = YES. @property(nonatomic,readonly) BOOL animationInProgress; // Returns YES if there is an animation in progress. diff --git a/CardEngine/CETableView.h b/CardEngine/CETableView.h index 81ed7ee..0a1d57e 100644 --- a/CardEngine/CETableView.h +++ b/CardEngine/CETableView.h @@ -16,8 +16,8 @@ NSInteger _animatingCount; } -@property(nonatomic,retain) NSString *portraitImagePath; // If nil no image is drawn. -@property(nonatomic,retain) NSString *landscapeImagePath; // If nil no image is drawn. +@property(nonatomic,strong) NSString *portraitImagePath; // If nil no image is drawn. +@property(nonatomic,strong) NSString *landscapeImagePath; // If nil no image is drawn. @property(nonatomic,readonly) BOOL animationInProgress; // Returns YES if there is an animation in progress. // Returns a singleton NSUndoManager. All card drags register with this undo manager. diff --git a/CardEngine/CETableView.m b/CardEngine/CETableView.m index 295f0bc..f6e1022 100644 --- a/CardEngine/CETableView.m +++ b/CardEngine/CETableView.m @@ -126,18 +126,6 @@ - (id) initWithFrame: (CGRect) frame return myself; } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - // Release instance variables. - [_portraitImagePath release]; - [_landscapeImagePath release]; - - // Super. - [super dealloc]; -} - // -------------------------------------------------------------------------------------------------------- drawGradient - (void) drawGradient @@ -271,7 +259,6 @@ - (BOOL) archiveStackStateWithIdentifier: (NSString *) identifier if (dictionary) { [defaults setObject: dictionary forKey: identifier]; - [dictionary release]; } return [defaults synchronize]; diff --git a/Classes/LabSolitaireAppDelegate.h b/Classes/LabSolitaireAppDelegate.h index e6031b4..eaf61b4 100644 --- a/Classes/LabSolitaireAppDelegate.h +++ b/Classes/LabSolitaireAppDelegate.h @@ -15,8 +15,8 @@ LabSolitaireViewController *_viewController; } -@property (nonatomic, retain) IBOutlet UIWindow *_window; -@property (nonatomic, retain) IBOutlet LabSolitaireViewController *_viewController; +@property (nonatomic, strong) IBOutlet UIWindow *_window; +@property (nonatomic, strong) IBOutlet LabSolitaireViewController *_viewController; @end diff --git a/Classes/LabSolitaireAppDelegate.m b/Classes/LabSolitaireAppDelegate.m index b5d5372..dc72c92 100644 --- a/Classes/LabSolitaireAppDelegate.m +++ b/Classes/LabSolitaireAppDelegate.m @@ -58,13 +58,4 @@ - (void) applicationDidReceiveMemoryWarning: (UIApplication *) application printf ("applicationDidReceiveMemoryWarning\n"); } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - [_viewController release]; - [_window release]; - [super dealloc]; -} - @end diff --git a/Classes/LocalPlayer.h b/Classes/LocalPlayer.h index 706f3be..f9c62f0 100644 --- a/Classes/LocalPlayer.h +++ b/Classes/LocalPlayer.h @@ -22,7 +22,7 @@ @property(nonatomic,readonly) NSString *alias; // Only valid if using Game Center. Must be authenticated. @property(nonatomic,readonly) BOOL authenticated; // Returns YES if authenticated (always YES if local). @property(nonatomic,readonly) BOOL usingGameCenter; // Returns YES if LocalPlayer is from Game Center. -@property(nonatomic,assign) id delegate; // Delegate called for asynchronous completions. +@property(nonatomic,weak) id delegate; // Delegate called for asynchronous completions. // Creates a LocalPlayer object. If Game Center is available, LocalPlayer initialized with the local player. Otherwise // NSUserDefaults will be used to post and retrieve scores. diff --git a/Classes/LocalPlayer.m b/Classes/LocalPlayer.m index ea2cd33..462d264 100644 --- a/Classes/LocalPlayer.m +++ b/Classes/LocalPlayer.m @@ -43,18 +43,6 @@ - (id) init return myself; } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - // Release instance vars. - [_playerID release]; - [_alias release]; - - // Super. - [super dealloc]; -} - // ------------------------------------------------------------------------------------------ postLocalScore:forCategory - (void) postLocalScore: (NSInteger) score forCategory: (NSString *) category @@ -128,7 +116,7 @@ - (BOOL) postLeaderboardScore: (NSInteger) score forCategory: (NSString *) categ success = [defaults synchronize]; // Create object to report score. Assign points. - scoreReporter = [[[GKScore alloc] initWithLeaderboardIdentifier: category] autorelease]; + scoreReporter = [[GKScore alloc] initWithLeaderboardIdentifier: category]; scoreReporter.value = score; // Report score. @@ -514,16 +502,8 @@ - (void) authenticateLocalPlayer } else { - if (_playerID) - { - [_playerID release]; - _playerID = nil; - } - if (_alias) - { - [_alias release]; - _alias = nil; - } + _playerID = nil; + _alias = nil; _authenticated = YES; _usingGameCenter = NO; diff --git a/main.m b/main.m index 67ee62b..9e2287d 100644 --- a/main.m +++ b/main.m @@ -7,12 +7,10 @@ int main (int argc, char *argv[]) -{ - NSAutoreleasePool *pool; - - pool = [[NSAutoreleasePool alloc] init]; - int retVal = UIApplicationMain (argc, argv, nil, @"LabSolitaireAppDelegate"); - [pool release]; - - return retVal; +{ + @autoreleasepool + { + int retVal = UIApplicationMain (argc, argv, nil, @"LabSolitaireAppDelegate"); + return retVal; + } } From 49b42afbec1cf1233b8ac1e9125eab4ba1c217de Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2026 12:50:46 +0200 Subject: [PATCH 04/33] Migrate codebase from MRC to ARC - Remove retain/release/autorelease calls across CardEngine and Classes - Replace NSAutoreleasePool with @autoreleasepool in main.m - Convert @property (retain) to (strong) and delegate properties to (weak) - Preserve dealloc methods that perform observer/timer cleanup - CocosDenshion remains under MRC via -fno-objc-arc compiler flag --- CardEngine/CEStackView.m | 113 +++++++------------------ Classes/LabSolitaireViewController.m | 21 +---- LabSolitaire.xcodeproj/project.pbxproj | 4 - 3 files changed, 35 insertions(+), 103 deletions(-) diff --git a/CardEngine/CEStackView.m b/CardEngine/CEStackView.m index aeea8c8..62db5e2 100644 --- a/CardEngine/CEStackView.m +++ b/CardEngine/CEStackView.m @@ -64,7 +64,7 @@ - (id) initWithFrame: (CGRect) frame _borderColor = [[UIColor alloc] initWithRed: 1. green: 1. blue: 1. alpha: 0.5]; _fillColor = [[UIColor alloc] initWithRed: 0.3 green: 0.8 blue: 0.3 alpha: 0.5]; _highlightColor = [[UIColor alloc] initWithRed: 0.0 green: 0.0 blue: 0.0 alpha: 0.25]; - _labelFont = [[UIFont fontWithName: @"Arial" size: 32] retain]; + _labelFont = [UIFont fontWithName: @"Arial" size: 32]; _labelColor = [[UIColor alloc] initWithRed: 0.07 green: 0.34 blue: 0.10 alpha: 1.]; _layout = kCEStackViewLayoutStandardSpread; _cornerRadius = -1.0; @@ -83,7 +83,7 @@ - (id) initWithFrame: (CGRect) frame self.exclusiveTouch = YES; // <--- EXPERIMENT. // Add empty stack by default. - [self setStack: [[[CEStack alloc] init] autorelease]]; + [self setStack: [[CEStack alloc] init]]; bail: @@ -96,19 +96,6 @@ - (void) dealloc { // Stop listening for notifications. [[NSNotificationCenter defaultCenter] removeObserver: self]; - - // Release instance vars. - [_borderColor release]; - [_fillColor release]; - [_highlightColor release]; - [_label release]; - [_labelFont release]; - [_labelColor release]; - [_cardViews release]; - [_stack release]; - - // Super. - [super dealloc]; } // ------------------------------------------------------------------------------------------------------------ isOpaque @@ -147,9 +134,8 @@ - (void) setStack: (CEStack *) stack [[NSNotificationCenter defaultCenter] removeObserver: self name: @"StackDidFlipCard" object: _stack]; [[NSNotificationCenter defaultCenter] removeObserver: self name: @"StackDidChangeOrder" object: _stack]; - // Release, retain, assign. - [_stack release]; - _stack = [stack retain]; + // Assign. + _stack = stack; // Listen for changes in the stack. [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector (stackChangedCount:) @@ -221,9 +207,8 @@ - (void) setBorderColor: (UIColor *) color if (color == _borderColor) return; - // Release, retain, assign. - [_borderColor release]; - _borderColor = [color retain]; + // Assign. + _borderColor = color; // Redraw. [self setNeedsDisplay]; @@ -245,9 +230,8 @@ - (void) setFillColor: (UIColor *) color if (color == _fillColor) return; - // Release, retain, assign. - [_fillColor release]; - _fillColor = [color retain]; + // Assign. + _fillColor = color; // Redraw. [self setNeedsDisplay]; @@ -269,9 +253,8 @@ - (void) setLabel: (NSString *) label if (label == _label) return; - // Release, retain, assign. - [_label release]; - _label = [label retain]; + // Assign. + _label = label; // Redraw. [self setNeedsDisplay]; @@ -293,9 +276,8 @@ - (void) setLabelFont: (UIFont *) font if (font == _labelFont) return; - // Release, retain, assign. - [_labelFont release]; - _labelFont = [font retain]; + // Assign. + _labelFont = font; // Redraw. [self setNeedsDisplay]; @@ -317,9 +299,8 @@ - (void) setLabelColor: (UIColor *) color if (color == _labelColor) return; - // Release, retain, assign. - [_labelColor release]; - _labelColor = [color retain]; + // Assign. + _labelColor = color; // Redraw. [self setNeedsDisplay]; @@ -509,9 +490,9 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B NSUInteger destNumCards; CGRect destRect; - // Create animation dictionary. Animation completion will release dictionary. + // Create animation dictionary. dictionary = [[NSMutableDictionary alloc] initWithCapacity: 3]; - + // Add animation type key to dictionary. [dictionary setObject: @"moveCardFromPointToPoint" forKey: @"type"]; @@ -550,9 +531,8 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B [sourceView setHidden: YES]; // Remove card from our own stack. - [card retain]; [_stack removeCard: card]; - + // Promise a card. [[stack stack] promiseCard: card]; @@ -564,13 +544,11 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B abortAnimation: // Remove card from our own stack. - [card retain]; [_stack removeCard: card]; - + // Flip and add card to destination stack. [card setFaceUp: faceUp]; [[stack stack] addCard: card]; - [card release]; } // Notify observers that a card is being dragged. @@ -608,9 +586,9 @@ - (void) flipCard: (CECard *) card faceUp: (BOOL) faceUp duration: (NSTimeInterv CECardView *sourceView; CGRect frame; - // Create animation dictionary. Animation completion will release dictionary. + // Create animation dictionary. dictionary = [[NSMutableDictionary alloc] initWithCapacity: 3]; - + // Add animation type key to dictionary. [dictionary setObject: @"flipCard" forKey: @"type"]; @@ -703,9 +681,8 @@ - (void) drawRect: (CGRect) rect NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; style.lineBreakMode = NSLineBreakByTruncatingMiddle; - [_label drawAtPoint: origin withAttributes: [NSDictionary dictionaryWithObjectsAndKeys: _labelFont, NSFontAttributeName, + [_label drawAtPoint: origin withAttributes: [NSDictionary dictionaryWithObjectsAndKeys: _labelFont, NSFontAttributeName, style, NSParagraphStyleAttributeName, nil]]; - [style release]; } // Draw border. @@ -1298,7 +1275,6 @@ - (void) createDraggedCardViews: (CGPoint) offset // Add to our array. [_draggedCardViews addObject: draggedCard]; - [draggedCard release]; } bail: @@ -1677,9 +1653,6 @@ - (void) registerDealCard: (CECard *) card stackView: (CEStackView *) stack dura // Pass the whole dictionary to the undo manager. [[CETableView sharedCardUndoManager] registerUndoWithTarget: stack selector: @selector (undoDealCard:) object: dictionary]; - - // Clean up. - [dictionary release]; } // ------------------------------------------------------------------------------------------- registerFlipCard:duration @@ -1698,9 +1671,6 @@ - (void) registerFlipCard: (CECard *) card duration: (NSTimeInterval) duration // Pass the whole dictionary to the undo manager. [[CETableView sharedCardUndoManager] registerUndoWithTarget: self selector: @selector (undoFlipCard:) object: dictionary]; - - // Clean up. - [dictionary release]; } // -------------------------------------------------------------------------------------------------------- undoDealCard @@ -2184,9 +2154,8 @@ - (void) layoutForStack if (!_orderly) cardView.transform = cardView.card.transform; [_cardViews addObject: cardView]; - [cardView release]; } - + // Create the top playing card view. frame = [self boundsForCardAtIndex: count - 1 forCount: count]; cardView = [[[self cardViewClass] alloc] initWithFrame: frame]; @@ -2198,8 +2167,7 @@ - (void) layoutForStack else [cardView setLabel: nil]; [_cardViews addObject: cardView]; - [cardView release]; - + // stack, simple bounding rect. _cardBoundingRect = frame; } @@ -2229,8 +2197,7 @@ - (void) layoutForSpread if (!_orderly) cardView.transform = cardView.card.transform; [_cardViews addObject: cardView]; - [cardView release]; - + // Get the union of all card bounds. _cardBoundingRect = CGRectUnion (_cardBoundingRect, frame); } @@ -2261,8 +2228,7 @@ - (void) layoutForColumn if (!_orderly) cardView.transform = cardView.card.transform; [_cardViews addObject: cardView]; - [cardView release]; - + // Get the union of all card bounds. _cardBoundingRect = CGRectUnion (_cardBoundingRect, frame); } @@ -2330,8 +2296,7 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary [tempView setCard: tempCard]; if (!_orderly) tempView.transform = tempCard.transform; - [tempCard release]; - + // Set card image for view. UIGraphicsBeginImageContext (frame.size); if (cardIsFaceUp) @@ -2340,7 +2305,7 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary [tempView drawCardBack]; [imageView setImage: UIGraphicsGetImageFromCurrentImageContext ()]; UIGraphicsEndImageContext (); - + // If the card is to flip, create a secondary image view for the card. if (willFlip) { @@ -2389,14 +2354,10 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary } [dictionary setObject: imageView forKey: @"view"]; - + // Set animation curve. [dictionary setObject: [NSNumber numberWithInt: UIViewAnimationCurveEaseIn] forKey: @"curve"]; - - // Done with temporary views. - [tempView release]; - [imageView release]; - + // Call delegate animation begin routine. if ((_delegate) && ([_delegate respondsToSelector: @selector (stackView:beginAnimatingCardMove:)])) [_delegate stackView: self beginAnimatingCardMove: card]; @@ -2537,7 +2498,6 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary // Flip and add card to destination stack. [card setFaceUp: faceUp]; [destStack promiseKeptForCard: card]; - [card release]; // Call delegate animation completion routine. if ((_delegate) && ([_delegate respondsToSelector: @selector (stackView:finishedAnimatingCardMove:)])) @@ -2553,11 +2513,8 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary [cardTable stackView: self finishedAnimatingCardMove: card]; // Notify observers that the card animating has landed. - [[NSNotificationCenter defaultCenter] postNotificationName: StackViewCardReleasedNotification + [[NSNotificationCenter defaultCenter] postNotificationName: StackViewCardReleasedNotification object: self userInfo: nil]; - - // Done. - [dictionary autorelease]; } } else if ([[dictionary objectForKey: @"type"] isEqualToString: @"flipCard"]) @@ -2607,8 +2564,7 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary [tempView setCard: tempCard]; if (!_orderly) tempView.transform = tempCard.transform; - [tempCard release]; - + // Create an image for the card. UIGraphicsBeginImageContext (frame.size); if (cardIsFaceUp) @@ -2648,10 +2604,6 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary // Set animation curve. [dictionary setObject: [NSNumber numberWithInt: UIViewAnimationCurveEaseIn] forKey: @"curve"]; - // Done with temporary views. - [tempView release]; - [imageView release]; - // Call delegate animation begin routine. if ((_delegate) && ([_delegate respondsToSelector: @selector (stackView:beginAnimatingCardFlip:)])) [_delegate stackView: self beginAnimatingCardFlip: card]; @@ -2727,9 +2679,6 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary cardTable = [self enclosingCETableView]; if (cardTable) [cardTable stackView: self finishedAnimatingCardFlip: card]; - - // Done. - [dictionary autorelease]; } } } diff --git a/Classes/LabSolitaireViewController.m b/Classes/LabSolitaireViewController.m index b1326e8..6fac550 100644 --- a/Classes/LabSolitaireViewController.m +++ b/Classes/LabSolitaireViewController.m @@ -727,7 +727,6 @@ - (void) createCardTableLayout [_cellViews[i] setIdentifier: @"Cell"]; [_cellViews[i] setArchiveIdentifier: [NSString stringWithFormat: @"Cell%d", i]]; [(CETableView *) self.view addSubview: _cellViews[i]]; - [_cellViews[i] release]; } // Create foundations. @@ -753,7 +752,6 @@ - (void) createCardTableLayout [_foundationViews[i] setIdentifier: @"Foundation"]; [_foundationViews[i] setArchiveIdentifier: [NSString stringWithFormat: @"Foundation%d", i]]; [(CETableView *) self.view addSubview: _foundationViews[i]]; - [_foundationViews[i] release]; } // Create tableau. @@ -781,7 +779,6 @@ - (void) createCardTableLayout [_tableauViews[i] setArchiveIdentifier: [NSString stringWithFormat: @"Tableau%d", i]]; [_tableauViews[i] setOrderly: NO]; [(CETableView *) self.view addSubview: _tableauViews[i]]; - [_tableauViews[i] release]; } // Layout the buttons. @@ -958,7 +955,6 @@ - (void) new: (id) sender cancelButtonTitle: NEW_GAME_CANCEL_BUTTON otherButtonTitles: NEW_GAME_BUTTON, nil]; alert.tag = kResetTableAlertTag; [alert show]; - [alert release]; } } @@ -1019,7 +1015,6 @@ - (void) undoAll: (id) sender cancelButtonTitle: UNDO_CANCEL_BUTTON otherButtonTitles: UNDO_ALL_BUTTON, nil]; alert.tag = kUndoAllAlertTag; [alert show]; - [alert release]; } } @@ -1888,11 +1883,9 @@ - (void) viewDidLoad - (void) viewDidUnload { - // Release any retained subviews of the main view. - // e.g. self.myOutlet = nil; - [_newButton release]; - [_undoButton release]; - [_infoButton release]; + _newButton = nil; + _undoButton = nil; + _infoButton = nil; } // ------------------------------------------------------------------------------------------------------------- dealloc @@ -1901,7 +1894,7 @@ - (void) dealloc { // No more observing. [[NSNotificationCenter defaultCenter] removeObserver: self]; - + // Clean up timers. if (_putawayTimer) { @@ -1913,9 +1906,6 @@ - (void) dealloc [_undoHeldTimer invalidate]; } _undoHeldTimer = nil; - - // Super. - [super dealloc]; } @@ -2421,7 +2411,6 @@ - (void) localPlayer: (LocalPlayer *) player failedAuthenticationWithError: (NSE [_leaderboardPlayerIDs removeAllObjects]; [_leaderboardGamesPlayed removeAllObjects]; [_leaderboardGamesWon removeAllObjects]; - [_leaderboardAliases release]; _leaderboardAliases = nil; _playerLeaderboardIndex = NSNotFound; @@ -2524,7 +2513,6 @@ - (void) localPlayer: (LocalPlayer *) player retrievedLeaderboardScores: (NSArra } else { - [_leaderboardAliases release]; _leaderboardAliases = nil; [self updateGlobalScoresInterface]; } @@ -2557,7 +2545,6 @@ - (void) localPlayer: (LocalPlayer *) player retrievedLeaderboardScoreForLocalPl - (void) localPlayer: (LocalPlayer *) player retrievedAliasesForPlayerIDs: (NSArray *) aliases { - [_leaderboardAliases release]; _leaderboardAliases = nil; if (aliases) _leaderboardAliases = [aliases copy]; diff --git a/LabSolitaire.xcodeproj/project.pbxproj b/LabSolitaire.xcodeproj/project.pbxproj index d985466..5fbf4f9 100755 --- a/LabSolitaire.xcodeproj/project.pbxproj +++ b/LabSolitaire.xcodeproj/project.pbxproj @@ -99,7 +99,6 @@ 26AE5BE31442601C00570ABC /* Highlight.png in Resources */ = {isa = PBXBuildFile; fileRef = 26AE5BE21442601C00570ABC /* Highlight.png */; }; 26AE5BF41442926100570ABC /* UnderlineWide.png in Resources */ = {isa = PBXBuildFile; fileRef = 26AE5BF31442926100570ABC /* UnderlineWide.png */; }; 26AE5BF6144292AF00570ABC /* UnderlineNarrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 26AE5BF5144292AF00570ABC /* UnderlineNarrow.png */; }; - 26B1E3E3131D608900DFA4EA /* iTunesArtwork in Resources */ = {isa = PBXBuildFile; fileRef = 26B1E3E1131D608900DFA4EA /* iTunesArtwork */; }; 26B1E41D131D6E6900DFA4EA /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 26B1E41C131D6E6900DFA4EA /* Icon-72.png */; }; 26B3E29C148404EC00C3459E /* CDAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E294148404EC00C3459E /* CDAudioManager.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 26B3E29D148404EC00C3459E /* CDOpenALSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E297148404EC00C3459E /* CDOpenALSupport.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; @@ -316,7 +315,6 @@ 26AE5BE21442601C00570ABC /* Highlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Highlight.png; sourceTree = ""; }; 26AE5BF31442926100570ABC /* UnderlineWide.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = UnderlineWide.png; sourceTree = ""; }; 26AE5BF5144292AF00570ABC /* UnderlineNarrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = UnderlineNarrow.png; sourceTree = ""; }; - 26B1E3E1131D608900DFA4EA /* iTunesArtwork */ = {isa = PBXFileReference; lastKnownFileType = text; path = iTunesArtwork; sourceTree = ""; }; 26B1E41C131D6E6900DFA4EA /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-72.png"; sourceTree = ""; }; 26B3E293148404EC00C3459E /* CDAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDAudioManager.h; sourceTree = ""; }; 26B3E294148404EC00C3459E /* CDAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDAudioManager.m; sourceTree = ""; }; @@ -775,7 +773,6 @@ 26A292FE1509E232004F24F2 /* Default-Portrait@2x~ipad.png */, 26F27B4C13F4592B00AB6EC1 /* Default-Landscape~ipad.png */, 26A292FC1509E189004F24F2 /* Default-Landscape@2x~ipad.png */, - 26B1E3E1131D608900DFA4EA /* iTunesArtwork */, ); name = Resources; sourceTree = ""; @@ -922,7 +919,6 @@ 2638162B131CDB73004E2983 /* NewSelectedP.png in Resources */, 2638162C131CDB73004E2983 /* UndoSelectedL.png in Resources */, 2638162D131CDB73004E2983 /* UndoSelectedP.png in Resources */, - 26B1E3E3131D608900DFA4EA /* iTunesArtwork in Resources */, 26B1E41D131D6E6900DFA4EA /* Icon-72.png in Resources */, 268F0E8C131F3E22000DC644 /* Line.png in Resources */, 268F0EA0131F4525000DC644 /* CheckNo.png in Resources */, From 2a022b3e7e31d21c66d2ba4564fe6ceb49f4e571 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2026 13:13:16 +0200 Subject: [PATCH 05/33] Replace missing CocosDenshion library with AVAudioPlayer wrapper - Add LSAudioEngine class providing the same minimal API surface (sharedEngine, preloadEffect:, playEffect:) used by the app - Use AVAudioPlayer with AVAudioSessionCategoryAmbient so sounds mix with other audio and respect the silent switch - Rewrite LabSolitaireViewController.m to use LSAudioEngine - Remove all CocosDenshion source file references from the project --- Classes/LSAudioEngine.h | 13 ++++ Classes/LSAudioEngine.m | 98 ++++++++++++++++++++++++++ Classes/LabSolitaireViewController.m | 58 +++++++-------- LabSolitaire.xcodeproj/project.pbxproj | 40 ++--------- 4 files changed, 146 insertions(+), 63 deletions(-) create mode 100644 Classes/LSAudioEngine.h create mode 100644 Classes/LSAudioEngine.m diff --git a/Classes/LSAudioEngine.h b/Classes/LSAudioEngine.h new file mode 100644 index 0000000..8842fc9 --- /dev/null +++ b/Classes/LSAudioEngine.h @@ -0,0 +1,13 @@ +// ===================================================================================================================== +// LSAudioEngine.h +// ===================================================================================================================== + +#import + +@interface LSAudioEngine : NSObject + ++ (instancetype)sharedEngine; +- (void)preloadEffect:(NSString *)filename; +- (void)playEffect:(NSString *)filename; + +@end diff --git a/Classes/LSAudioEngine.m b/Classes/LSAudioEngine.m new file mode 100644 index 0000000..89c480e --- /dev/null +++ b/Classes/LSAudioEngine.m @@ -0,0 +1,98 @@ +// ===================================================================================================================== +// LSAudioEngine.m +// ===================================================================================================================== + +#import +#import "LSAudioEngine.h" + + +@implementation LSAudioEngine +{ + NSMutableDictionary *_players; +} + ++ (instancetype)sharedEngine +{ + static LSAudioEngine *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[LSAudioEngine alloc] init]; + }); + return sharedInstance; +} + +- (instancetype)init +{ + self = [super init]; + if (self) + { + _players = [[NSMutableDictionary alloc] init]; + + NSError *error = nil; + [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&error]; + if (error) + NSLog(@"LSAudioEngine: failed to set audio session category: %@", error); + } + return self; +} + +- (NSURL *)urlForFilename:(NSString *)filename +{ + NSString *name = [filename stringByDeletingPathExtension]; + NSString *ext = [filename pathExtension]; + NSURL *url = [[NSBundle mainBundle] URLForResource:name withExtension:ext]; + if (url == nil) + NSLog(@"LSAudioEngine: sound file not found in bundle: %@", filename); + return url; +} + +- (void)preloadEffect:(NSString *)filename +{ + if (_players[filename] != nil) + return; + + NSURL *url = [self urlForFilename:filename]; + if (url == nil) + return; + + NSError *error = nil; + AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error]; + if (player == nil) + { + NSLog(@"LSAudioEngine: failed to create player for %@: %@", filename, error); + return; + } + + [player prepareToPlay]; + _players[filename] = player; +} + +- (void)playEffect:(NSString *)filename +{ + AVAudioPlayer *player = _players[filename]; + + if (player == nil) + { + [self preloadEffect:filename]; + player = _players[filename]; + if (player == nil) + return; + } + + if ([player isPlaying]) + { + NSError *error = nil; + AVAudioPlayer *duplicate = [[AVAudioPlayer alloc] initWithContentsOfURL:[player url] error:&error]; + if (duplicate) + { + [duplicate play]; + } + } + else + { + player.currentTime = 0; + [player play]; + } +} + +@end diff --git a/Classes/LabSolitaireViewController.m b/Classes/LabSolitaireViewController.m index 6fac550..732f6f7 100644 --- a/Classes/LabSolitaireViewController.m +++ b/Classes/LabSolitaireViewController.m @@ -7,7 +7,7 @@ #import "CEStackViewPrivate.h" #import "LabSolitaireViewController.h" #import "LSStackView.h" -#import "SimpleAudioEngine.h" +#import "LSAudioEngine.h" #define DISPLAY_OUTLINE_IN_TABLEAU 0 @@ -818,20 +818,20 @@ - (void) createCardTableLayout _worriedCards = [[NSMutableArray alloc] initWithCapacity: 3]; // Load sounds. - [[SimpleAudioEngine sharedEngine] preloadEffect: @"Shuffle.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"Shuffle.wav"]; for (i = 0; i < kNumCardDrawSounds; i++) { - [[SimpleAudioEngine sharedEngine] preloadEffect: [NSString stringWithFormat: @"CardDraw%d.wav", i]]; + [[LSAudioEngine sharedEngine] preloadEffect: [NSString stringWithFormat: @"CardDraw%d.wav", i]]; } for (i = 0; i < kNumCardPlaceSounds; i++) { - [[SimpleAudioEngine sharedEngine] preloadEffect: [NSString stringWithFormat: @"CardPlace%d.wav", i]]; + [[LSAudioEngine sharedEngine] preloadEffect: [NSString stringWithFormat: @"CardPlace%d.wav", i]]; } - [[SimpleAudioEngine sharedEngine] preloadEffect: @"ClickOpen.wav"]; - [[SimpleAudioEngine sharedEngine] preloadEffect: @"ClickClose.wav"]; - [[SimpleAudioEngine sharedEngine] preloadEffect: @"Blip.wav"]; - [[SimpleAudioEngine sharedEngine] preloadEffect: @"Buzz.wav"]; - [[SimpleAudioEngine sharedEngine] preloadEffect: @"Babip.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"ClickClose.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"Blip.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"Buzz.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"Babip.wav"]; skipAudio: @@ -933,14 +933,14 @@ - (void) new: (id) sender { if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } if ((_playedAtleastOneCard == NO) || ([self allCardsArePutAway])) { if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; } // If the game is over, no need for alert. @@ -979,11 +979,11 @@ - (void) undo: (id) sender { if ([[CETableView sharedCardUndoManager] canUndo]) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Blip.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Blip.wav"]; } else { - [[SimpleAudioEngine sharedEngine] playEffect: @"Buzz.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Buzz.wav"]; } } @@ -1005,7 +1005,7 @@ - (void) undoAll: (id) sender if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } _undoAllAlertOpen = YES; @@ -1294,7 +1294,7 @@ - (void) info: (id) sender { if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } _infoViewIsOpen = YES; @@ -1484,7 +1484,7 @@ - (void) _addInfoSubview: (UIView *) subview { if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } // Switch to display the subview. @@ -1535,7 +1535,7 @@ - (void) closeInfo: (id) sender if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickClose.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickClose.wav"]; } [_aboutViewController dismissViewControllerAnimated: YES completion: nil]; @@ -1592,12 +1592,12 @@ - (void) toggleAutoPutaway: (id) sender // Sound effect. if ((_playSounds) && (_autoPutaway == YES)) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } if ((_playSounds) && (_autoPutaway == NO)) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickClose.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickClose.wav"]; } // Update UI. @@ -1624,7 +1624,7 @@ - (void) selectAutoPutawayMode: (id) sender if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } // Update UI. @@ -1648,7 +1648,7 @@ - (void) toggleSound: (id) sender // Sound effect. if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } // Update UI. @@ -1696,7 +1696,7 @@ - (void) selectLeaderboardScope: (id) sender if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } // Update UI. @@ -1714,7 +1714,7 @@ - (void) openGameOverView: (id) sender // Player won sound. if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Babip.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Babip.wav"]; } // Switch to display the "game over view". @@ -1768,7 +1768,7 @@ - (void) animationStopped: (NSString *) animationID finished: (NSNumber *) finis // Player won sound. if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Babip.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Babip.wav"]; } } } @@ -1809,7 +1809,7 @@ - (void) animationStopped: (NSString *) animationID finished: (NSNumber *) finis // Shuffle sound. if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; } // Deal new hand. @@ -1920,7 +1920,7 @@ - (void) alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) { if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; } [self resetTable: YES]; @@ -1934,7 +1934,7 @@ - (void) alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) { if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; } [self resetTable: NO]; @@ -2355,7 +2355,7 @@ - (void) cardPickedUp: (NSNotification *) notification { // Play card drawn sound. if (_playSounds) - [[SimpleAudioEngine sharedEngine] playEffect: [NSString stringWithFormat: @"CardDraw%d.wav", CERandomInt (kNumCardDrawSounds)]]; + [[LSAudioEngine sharedEngine] playEffect: [NSString stringWithFormat: @"CardDraw%d.wav", CERandomInt (kNumCardDrawSounds)]]; } // -------------------------------------------------------------------------------------------------------- cardReleased @@ -2370,7 +2370,7 @@ - (void) cardReleased: (NSNotification *) notification // Play card placed sound. if (_playSounds) - [[SimpleAudioEngine sharedEngine] playEffect: [NSString stringWithFormat: @"CardPlace%d.wav", CERandomInt (kNumCardPlaceSounds)]]; + [[LSAudioEngine sharedEngine] playEffect: [NSString stringWithFormat: @"CardPlace%d.wav", CERandomInt (kNumCardPlaceSounds)]]; } // -------------------------------------------------------------------------------------------------------- putawayTimer diff --git a/LabSolitaire.xcodeproj/project.pbxproj b/LabSolitaire.xcodeproj/project.pbxproj index 5fbf4f9..f2988f0 100755 --- a/LabSolitaire.xcodeproj/project.pbxproj +++ b/LabSolitaire.xcodeproj/project.pbxproj @@ -100,10 +100,6 @@ 26AE5BF41442926100570ABC /* UnderlineWide.png in Resources */ = {isa = PBXBuildFile; fileRef = 26AE5BF31442926100570ABC /* UnderlineWide.png */; }; 26AE5BF6144292AF00570ABC /* UnderlineNarrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 26AE5BF5144292AF00570ABC /* UnderlineNarrow.png */; }; 26B1E41D131D6E6900DFA4EA /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 26B1E41C131D6E6900DFA4EA /* Icon-72.png */; }; - 26B3E29C148404EC00C3459E /* CDAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E294148404EC00C3459E /* CDAudioManager.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 26B3E29D148404EC00C3459E /* CDOpenALSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E297148404EC00C3459E /* CDOpenALSupport.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 26B3E29E148404EC00C3459E /* CocosDenshion.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E299148404EC00C3459E /* CocosDenshion.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 26B3E29F148404EC00C3459E /* SimpleAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E29B148404EC00C3459E /* SimpleAudioEngine.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 26B3E2A91484084700C3459E /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B3E2A81484084700C3459E /* OpenAL.framework */; }; 26B3E2AB1484086600C3459E /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B3E2AA1484086600C3459E /* AudioToolbox.framework */; }; 26B6709914EF69FA00C8E6B2 /* 1C@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 26B6708914EF69FA00C8E6B2 /* 1C@2x.png */; }; @@ -215,6 +211,7 @@ 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; }; 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; }; 28D7ACF80DDB3853001CB0EB /* LabSolitaireViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* LabSolitaireViewController.m */; }; + 4C1616F72FA61289002DEE82 /* LSAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -316,15 +313,6 @@ 26AE5BF31442926100570ABC /* UnderlineWide.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = UnderlineWide.png; sourceTree = ""; }; 26AE5BF5144292AF00570ABC /* UnderlineNarrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = UnderlineNarrow.png; sourceTree = ""; }; 26B1E41C131D6E6900DFA4EA /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-72.png"; sourceTree = ""; }; - 26B3E293148404EC00C3459E /* CDAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDAudioManager.h; sourceTree = ""; }; - 26B3E294148404EC00C3459E /* CDAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDAudioManager.m; sourceTree = ""; }; - 26B3E295148404EC00C3459E /* CDConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDConfig.h; sourceTree = ""; }; - 26B3E296148404EC00C3459E /* CDOpenALSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDOpenALSupport.h; sourceTree = ""; }; - 26B3E297148404EC00C3459E /* CDOpenALSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDOpenALSupport.m; sourceTree = ""; }; - 26B3E298148404EC00C3459E /* CocosDenshion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocosDenshion.h; sourceTree = ""; }; - 26B3E299148404EC00C3459E /* CocosDenshion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocosDenshion.m; sourceTree = ""; }; - 26B3E29A148404EC00C3459E /* SimpleAudioEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAudioEngine.h; sourceTree = ""; }; - 26B3E29B148404EC00C3459E /* SimpleAudioEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAudioEngine.m; sourceTree = ""; }; 26B3E2A81484084700C3459E /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; }; 26B3E2AA1484086600C3459E /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 26B6708914EF69FA00C8E6B2 /* 1C@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "1C@2x.png"; sourceTree = ""; }; @@ -452,6 +440,8 @@ 28D7ACF60DDB3853001CB0EB /* LabSolitaireViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabSolitaireViewController.h; sourceTree = ""; }; 28D7ACF70DDB3853001CB0EB /* LabSolitaireViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LabSolitaireViewController.m; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 4C1616F52FA61289002DEE82 /* LSAudioEngine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LSAudioEngine.h; sourceTree = ""; }; + 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LSAudioEngine.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -475,6 +465,8 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( + 4C1616F52FA61289002DEE82 /* LSAudioEngine.h */, + 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */, 26ED7011143CD2BA00E06F05 /* LocalPlayer.h */, 26ED7010143CD2BA00E06F05 /* LocalPlayer_priv.h */, 26ED7012143CD2BA00E06F05 /* LocalPlayer.m */, @@ -519,22 +511,6 @@ path = Sounds; sourceTree = ""; }; - 26B3E292148404EC00C3459E /* CocosDenshion */ = { - isa = PBXGroup; - children = ( - 26B3E293148404EC00C3459E /* CDAudioManager.h */, - 26B3E294148404EC00C3459E /* CDAudioManager.m */, - 26B3E295148404EC00C3459E /* CDConfig.h */, - 26B3E296148404EC00C3459E /* CDOpenALSupport.h */, - 26B3E297148404EC00C3459E /* CDOpenALSupport.m */, - 26B3E298148404EC00C3459E /* CocosDenshion.h */, - 26B3E299148404EC00C3459E /* CocosDenshion.m */, - 26B3E29A148404EC00C3459E /* SimpleAudioEngine.h */, - 26B3E29B148404EC00C3459E /* SimpleAudioEngine.m */, - ); - path = CocosDenshion; - sourceTree = ""; - }; 26DE145912F4902B00FF88C6 /* CardEngine */ = { isa = PBXGroup; children = ( @@ -738,7 +714,6 @@ 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { isa = PBXGroup; children = ( - 26B3E292148404EC00C3459E /* CocosDenshion */, 26DE145912F4902B00FF88C6 /* CardEngine */, 080E96DDFE201D6D7F000001 /* Classes */, 29B97315FDCFA39411CA2CEA /* Other Sources */, @@ -1050,6 +1025,7 @@ 1D3623260D0F684500981E51 /* LabSolitaireAppDelegate.m in Sources */, 28D7ACF80DDB3853001CB0EB /* LabSolitaireViewController.m in Sources */, 26DE146912F4902B00FF88C6 /* CECard.m in Sources */, + 4C1616F72FA61289002DEE82 /* LSAudioEngine.m in Sources */, 26DE146A12F4902B00FF88C6 /* CECardView.m in Sources */, 26DE146B12F4902B00FF88C6 /* CEStack.m in Sources */, 26DE146C12F4902B00FF88C6 /* CEStackView.m in Sources */, @@ -1058,10 +1034,6 @@ 26C84EA81301002000EB4D52 /* LSStackView.m in Sources */, 26ED7013143CD2BA00E06F05 /* LocalPlayer.m in Sources */, 26A13F9314438705000D8B18 /* CECardDealer.m in Sources */, - 26B3E29C148404EC00C3459E /* CDAudioManager.m in Sources */, - 26B3E29D148404EC00C3459E /* CDOpenALSupport.m in Sources */, - 26B3E29E148404EC00C3459E /* CocosDenshion.m in Sources */, - 26B3E29F148404EC00C3459E /* SimpleAudioEngine.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 13a8a0db17941bb081a20d487540113a2124d4b4 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2026 14:00:43 +0200 Subject: [PATCH 06/33] Remove obsolete CocosDenshion placeholder The placeholder listed missing third-party files that have now been replaced by LSAudioEngine. --- CocosDenshion/placeholder.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 CocosDenshion/placeholder.txt diff --git a/CocosDenshion/placeholder.txt b/CocosDenshion/placeholder.txt deleted file mode 100644 index 6123b80..0000000 --- a/CocosDenshion/placeholder.txt +++ /dev/null @@ -1,3 +0,0 @@ -These files from CocosDenshion here: - -SimpleAudioEngine.m SimpleAudioEngine.h CocosDenshion.h CocosDenshion.m CDOpenALSupport.m CDOpenALSupport.h CDAudioManager.m CDConfig.h CDAudioManager.h \ No newline at end of file From 806a3dcfd8d1a2aba1b863767848a61740eccf2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20M=C3=BCller-Hillebrand?= Date: Sat, 2 May 2026 14:04:05 +0200 Subject: [PATCH 07/33] Update screenshot link in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84b93bc..40270f6 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,4 @@ An iOS solitaire game (Freecell) for the iPad from 2010. Free Cell was one of my favorite solitaire card games. I wanted to make a very good implementation for the iPad that was also beautiful. -![Screenshot](https://github.com/softdorothy/LabSolitaire/blob/main/Screenshots/Screenshot%201.jpg) +![Screenshot](Screenshots/Screenshot%201.jpg) From 6f8b55b35c140cc8977855b5c03b0e831d214ca9 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2026 19:36:06 +0200 Subject: [PATCH 08/33] Fix remaining ARC and deprecated API issues to achieve clean build - Replace require/require_quiet macros with __Require/__Require_Quiet across 7 files (73 conversions); add #include where needed - Add __bridge casts in animation context handling (CEStackView.m, LabSolitaireViewController.m) - Annotate weak delegate ivars with __weak to match property attributes - Refactor 4 methods in LocalPlayer.m to use early return instead of goto-past-block, which is illegal under ARC - Replace deprecated applicationFrame and statusBarOrientation in CETableView.m Project now builds cleanly on Xcode 26 / iOS 17. --- CardEngine/CECard.m | 10 +-- CardEngine/CECardDealer.h | 2 +- CardEngine/CECardDealer.m | 2 +- CardEngine/CECardView.m | 22 +++--- CardEngine/CEStack.m | 68 ++++++++--------- CardEngine/CEStackView.h | 2 +- CardEngine/CEStackView.m | 38 +++++----- CardEngine/CETableView.m | 18 +++-- Classes/LabSolitaireViewController.m | 6 +- Classes/LocalPlayer.h | 2 +- Classes/LocalPlayer.m | 106 +++++++++++---------------- 11 files changed, 133 insertions(+), 143 deletions(-) diff --git a/CardEngine/CECard.m b/CardEngine/CECard.m index 7fae369..6dbdacd 100644 --- a/CardEngine/CECard.m +++ b/CardEngine/CECard.m @@ -34,8 +34,8 @@ + (NSUInteger) indexWithRank: (CERank) rank andSuit: (CESuit) suit NSUInteger index = 0; // Param check. - require ((rank >= kCERankAce) && (rank <= kCERankKing), bail); - require ((suit >= kCESuitDiamonds) && (suit <= kCESuitClubs), bail); + __Require ((rank >= kCERankAce) && (rank <= kCERankKing), bail); + __Require ((suit >= kCESuitDiamonds) && (suit <= kCESuitClubs), bail); // Compute index. index = (suit * 13) + rank; @@ -52,7 +52,7 @@ + (CERank) rankFromIndex: (NSUInteger) index CERank rank = 0; // Param check. - require ((index >= 1) && (index <= 52), bail); + __Require ((index >= 1) && (index <= 52), bail); // Modulo 13. rank = ((index - 1) % 13) + 1; @@ -69,7 +69,7 @@ + (CESuit) suitFromIndex: (NSUInteger) index CESuit suit = 0; // Param check. - require ((index >= 1) && (index <= 52), bail); + __Require ((index >= 1) && (index <= 52), bail); // Divide by 13. suit = (index - 1) / 13; @@ -187,7 +187,7 @@ - (id) initWithIndex: (NSUInteger) index // Super. myself = [super init]; - require (myself, bail); + __Require (myself, bail); // Assign instance variable. if (index == 0) diff --git a/CardEngine/CECardDealer.h b/CardEngine/CECardDealer.h index 2f7ea0a..2a5a8bf 100644 --- a/CardEngine/CECardDealer.h +++ b/CardEngine/CECardDealer.h @@ -22,7 +22,7 @@ BOOL _dealing; BOOL _enableUndoGrouping; NSTimer *_dealTimer; - id _delegate; + __weak id _delegate; } @property(nonatomic,strong,readonly) CEStackView *sourceStack; diff --git a/CardEngine/CECardDealer.m b/CardEngine/CECardDealer.m index 45a4402..018faea 100644 --- a/CardEngine/CECardDealer.m +++ b/CardEngine/CECardDealer.m @@ -34,7 +34,7 @@ - (id) init // Super. myself = [super init]; - require (myself, bail); + __Require (myself, bail); // Initialize instance variables. _duration = kDefaultDealAnimationDuration; diff --git a/CardEngine/CECardView.m b/CardEngine/CECardView.m index 27de256..dc94dcf 100644 --- a/CardEngine/CECardView.m +++ b/CardEngine/CECardView.m @@ -83,7 +83,7 @@ - (id) initWithFrame: (CGRect) frame // Super. myself = [super initWithFrame: frame]; - require (myself, bail); + __Require (myself, bail); // Initialize instance variables. _card = nil; @@ -290,25 +290,25 @@ - (void) drawCardFace // Get image URL from bundle. bundle = CFBundleGetMainBundle (); - require (bundle, bail); + __Require (bundle, bail); base = CFBundleCopyResourcesDirectoryURL (bundle); - require (base, bail); + __Require (base, bail); url = CFURLCreateWithFileSystemPathRelativeToBase (kCFAllocatorDefault, CFSTR ("Cards.pdf"), kCFURLPOSIXPathStyle, false, base); - require (url, bail); + __Require (url, bail); // Get PDF document. gCardPDFDocument = CGPDFDocumentCreateWithURL (url); } // Must have a PDF document by now. - require (gCardPDFDocument, bail); + __Require (gCardPDFDocument, bail); // Get the page of the PDF that corresponds to our card index. cardPage = CGPDFDocumentGetPage (gCardPDFDocument, [_card index]); - require (cardPage, bail); + __Require (cardPage, bail); transform = CGPDFPageGetDrawingTransform (cardPage, kCGPDFCropBox, bounds, 0, true); @@ -357,25 +357,25 @@ - (void) drawCardBack // Get image URL from bundle. bundle = CFBundleGetMainBundle (); - require (bundle, bail); + __Require (bundle, bail); base = CFBundleCopyResourcesDirectoryURL (bundle); - require (base, bail); + __Require (base, bail); url = CFURLCreateWithFileSystemPathRelativeToBase (kCFAllocatorDefault, CFSTR ("Cards.pdf"), kCFURLPOSIXPathStyle, false, base); - require (url, bail); + __Require (url, bail); // Get PDF document. gCardPDFDocument = CGPDFDocumentCreateWithURL (url); } // Must have a PDF document by now. - require (gCardPDFDocument, bail); + __Require (gCardPDFDocument, bail); // Get the page of the PDF that corresponds to our card index. cardPage = CGPDFDocumentGetPage (gCardPDFDocument, 53); - require (cardPage, bail); + __Require (cardPage, bail); transform = CGPDFPageGetDrawingTransform (cardPage, kCGPDFCropBox, bounds, 0, true); diff --git a/CardEngine/CEStack.m b/CardEngine/CEStack.m index d604c1a..16d0524 100644 --- a/CardEngine/CEStack.m +++ b/CardEngine/CEStack.m @@ -38,7 +38,7 @@ + (id) deckOfCards id deck; deck = [[CEStack alloc] init]; - require (deck, bail); + __Require (deck, bail); // Add deck of cards. for (i = 1; i <= 52; i++) @@ -63,7 +63,7 @@ - (id) init // Super. myself = [super init]; - require (myself, bail); + __Require (myself, bail); // Initialize instance variables. _cards = nil; @@ -98,8 +98,8 @@ - (CECard *) cardAtIndex: (NSUInteger) index CECard *card = nil; // NOP. - require (_cards, bail); - require (index < [_cards count], bail); + __Require (_cards, bail); + __Require (index < [_cards count], bail); // Return the object in our array at index. card = [(CardData *)[_cards objectAtIndex: index] card]; @@ -117,7 +117,7 @@ - (NSUInteger) indexForCard: (CECard *) card NSUInteger index = NSNotFound; // NOP. - require (card, bail); + __Require (card, bail); // Return the index of the object in our array. count = [self numberOfCards]; @@ -143,7 +143,7 @@ - (BOOL) stackContainsCard: (CECard *) card BOOL contains = NO; // Param check. - require (card, bail); + __Require (card, bail); // Is the card in our array? count = [self numberOfCards]; @@ -168,8 +168,8 @@ - (CECard *) topCard CECard *card = nil; // NOP. - require (_cards, bail); - require ([_cards count] > 0, bail); + __Require (_cards, bail); + __Require ([_cards count] > 0, bail); // Return the last object in our array. card = [(CardData *)[_cards lastObject] card]; @@ -217,7 +217,7 @@ - (NSArray *) cardArrayRepresentation - (void) addCard: (CECard *) card { // Param check. - require (card, bail); + __Require (card, bail); // Add the card. [self addCardWithoutNotification: card]; @@ -237,8 +237,8 @@ - (void) addCardsFromStack: (CEStack *) stack inRange: (NSRange) range NSUInteger i; // Param check. - require (stack, bail); - require ([stack numberOfCards] >= NSMaxRange (range), bail); + __Require (stack, bail); + __Require ([stack numberOfCards] >= NSMaxRange (range), bail); // Add the cards one at a time. for (i = range.location; i < NSMaxRange (range); i++) @@ -259,11 +259,11 @@ - (void) addAllCardsFromStack: (CEStack *) stack faceUp: (BOOL) faceUpOrDown NSUInteger count, i; // Param check. - require (stack, bail); + __Require (stack, bail); // How many cards are in stack passed in? count = [stack numberOfCards]; - require_quiet (count > 0, bail); + __Require_Quiet (count > 0, bail); // Add the cards one at a time. for (i = 0; i < count; i++) @@ -290,7 +290,7 @@ - (void) insertCard: (CECard *) card atIndex: (NSUInteger) index CardData *cardData; // Param check. - require (card, bail); + __Require (card, bail); // Create lazily. if (_cards == nil) @@ -346,11 +346,11 @@ - (void) addCardsFromCardArrayRepresentation: (NSArray *) array randomize: (BOOL NSUInteger count, i; // Param check. - require (array, bail); + __Require (array, bail); // See if there are any cards here. count = [array count]; - require_quiet (count > 0, bail); + __Require_Quiet (count > 0, bail); // For each card, add an NSNumber representing the value of the card to array. for (i = 0; i < count; i++) @@ -390,8 +390,8 @@ - (void) removeCard: (CECard *) card NSUInteger index; // Param check. - require (card, bail); - require (_cards, bail); + __Require (card, bail); + __Require (_cards, bail); // Skip out if this is not our card. if ([self stackContainsCard: card] == NO) @@ -399,7 +399,7 @@ - (void) removeCard: (CECard *) card // Get index for card we are going to remove. index = [self indexForCard: card]; - require (index != NSNotFound, bail); + __Require (index != NSNotFound, bail); // Remove card data object. [_cards removeObjectAtIndex: index]; @@ -416,8 +416,8 @@ - (void) removeCard: (CECard *) card - (void) removeCardsInRange: (NSRange) range { - require (_cards, bail); - require (NSMaxRange (range) <= [_cards count], bail); + __Require (_cards, bail); + __Require (NSMaxRange (range) <= [_cards count], bail); // Remove the card data objects. [_cards removeObjectsInRange: range]; @@ -435,8 +435,8 @@ - (void) removeCardsInRange: (NSRange) range - (void) removeAllCards { // Param check. - require (_cards, bail); - require ([_cards count] > 0, bail); + __Require (_cards, bail); + __Require ([_cards count] > 0, bail); // Remove all cards (empty the array). [_cards removeAllObjects]; @@ -455,8 +455,8 @@ - (void) removeAllCards - (void) flipCard: (CECard *) card faceUp: (BOOL) faceUpOrDown { // Param check. - require (card, bail); - require (_cards, bail); + __Require (card, bail); + __Require (_cards, bail); // Skip out if this is not our card. if ([self stackContainsCard: card] == NO) @@ -487,11 +487,11 @@ - (void) revealAllCards BOOL flippedACard = NO; // Param check. - require (_cards, bail); + __Require (_cards, bail); // How many cards? count = [_cards count]; - require (count > 0, bail); + __Require (count > 0, bail); // Set face up. for (i = 0; i < count; i++) @@ -553,7 +553,7 @@ - (void) shuffleWithSeed: (NSUInteger) seed NSUInteger count, i; // NOP. - require (_cards, bail); + __Require (_cards, bail); // Seed the psuedo-random number generator. _seedUsed = seed; @@ -561,7 +561,7 @@ - (void) shuffleWithSeed: (NSUInteger) seed // Swap each card with a random card. count = [_cards count]; - require (count > 1, bail); + __Require (count > 1, bail); // New shuffle algorithm: Fisher and Yates (also called the Knuth Shuffle). for (i = count - 1; i > 0; i--) @@ -602,14 +602,14 @@ - (void) shuffleDeckWithSeed: (NSUInteger) seed CEStack *tempDeck; // NOP. - require (_cards, bail); + __Require (_cards, bail); // Swap each card with a random card. count = [_cards count]; wLeft = count; // A current restriction is that the stack has exactly 52 cards. - require (count == 52, bail); + __Require (count == 52, bail); // Random seed. winSRand ((unsigned) seed); @@ -653,7 +653,7 @@ - (void) addCardWithoutNotification: (CECard *) card CardData *cardData; // Param check. - require (card, bail); + __Require (card, bail); // Create lazily. if (_cards == nil) @@ -676,7 +676,7 @@ - (void) promiseCard: (CECard *) card CardData *cardData; // Param check. - require (card, bail); + __Require (card, bail); // Create lazily. if (_cards == nil) @@ -703,7 +703,7 @@ - (void) promiseKeptForCard: (CECard *) card // Get the card index. index = [self indexForCard: card]; - require (index != NSNotFound, bail); + __Require (index != NSNotFound, bail); // Clear promised flag. [[_cards objectAtIndex: index] setPromised: NO]; diff --git a/CardEngine/CEStackView.h b/CardEngine/CEStackView.h index 088b4e2..b595051 100644 --- a/CardEngine/CEStackView.h +++ b/CardEngine/CEStackView.h @@ -64,7 +64,7 @@ enum NSInteger _highlightedViewIndex; // If our own stack is highlighted, the card view highlighted. CEStackDragPermissions _dragPermissions; // Simple drag permissions. More complex rules done via delegate. BOOL _allowsReordering; // Whether cards within a stack can be re-ordered by the user. - id _delegate; + __weak id _delegate; id _privateDelegate; int _touchState; // State of the touch event. NSUInteger _cardIndexRevealed; // Index of the card that is being revealed. diff --git a/CardEngine/CEStackView.m b/CardEngine/CEStackView.m index 62db5e2..af8686e 100644 --- a/CardEngine/CEStackView.m +++ b/CardEngine/CEStackView.m @@ -58,7 +58,7 @@ - (id) initWithFrame: (CGRect) frame // Super. myself = [super initWithFrame: frame]; - require (myself, bail); + __Require (myself, bail); // Default instance variable values. _borderColor = [[UIColor alloc] initWithRed: 1. green: 1. blue: 1. alpha: 0.5]; @@ -475,9 +475,9 @@ - (void) dealTopCardToStackView: (CEStackView *) destStack faceUp: (BOOL) faceUp - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (BOOL) faceUp duration: (NSTimeInterval) duration { // Param check. - require (stack, bail); - require (_stack, bail); - require ([_stack stackContainsCard: card], bail); + __Require (stack, bail); + __Require (_stack, bail); + __Require ([_stack stackContainsCard: card], bail); // Register for 'undo'. [self registerDealCard: card stackView: stack duration: duration]; @@ -504,7 +504,7 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B // Get card view that is going to animate. sourceView = [self cardViewForCard: card]; - require (sourceView, abortAnimation); + __Require (sourceView, abortAnimation); // Determine the coordinate to move from. point = sourceView.center; @@ -565,8 +565,8 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B - (void) flipCard: (CECard *) card faceUp: (BOOL) faceUp duration: (NSTimeInterval) duration { // Param check. - require (card, bail); - require (_stack, bail); + __Require (card, bail); + __Require (_stack, bail); // Skip out if this is not our card. if ([_stack stackContainsCard: card] == NO) @@ -599,7 +599,7 @@ - (void) flipCard: (CECard *) card faceUp: (BOOL) faceUp duration: (NSTimeInterv // Get the bounds for the flipping card. sourceView = [self cardViewForCard: card]; - require (sourceView, abortAnimation); + __Require (sourceView, abortAnimation); frame = [self convertRect: [sourceView frame] toView: [self superview]]; // Add frame to animation dictionary. @@ -1792,12 +1792,12 @@ - (BOOL) shouldRevealCardOnTouch: (UITouch *) touch NSUInteger cardIndex; // Sanity check (not needed I think). - require (_stack, bail); - require ([_stack numberOfCards] > 0, bail); + __Require (_stack, bail); + __Require ([_stack numberOfCards] > 0, bail); // Get card index for touch location. cardIndex = [self cardIndexToRevealAtLocation: [touch locationInView: self]]; - require (cardIndex != NSNotFound, bail); + __Require (cardIndex != NSNotFound, bail); // No reveal on top card. Otherwise, yes. if (cardIndex == ([_stack numberOfCards] - 1)) @@ -1882,13 +1882,13 @@ - (CECardView *) cardViewForCardIndex: (NSUInteger) index CECardView *view = nil; // Param check. - require (index != NSNotFound, bail); + __Require (index != NSNotFound, bail); // Stacked layout does not have a one-to-one correlation between views and cards. Special case. if (_layout == kCEStackViewLayoutStacked) { // Sanity check: index ought to correspond to the top card. - require ((index + 1) == [_stack numberOfCards], bail); + __Require ((index + 1) == [_stack numberOfCards], bail); // Top view is for top card. view = [_cardViews lastObject]; @@ -1896,10 +1896,10 @@ - (CECardView *) cardViewForCardIndex: (NSUInteger) index else { // Sanity check: index should be within range. - require (index < [_stack numberOfCards], bail); + __Require (index < [_stack numberOfCards], bail); // Second sanity check, index should be within range of the card views. - require (index < [_cardViews count], bail); + __Require (index < [_cardViews count], bail); // Card index and view order are one-to-one. view = [_cardViews objectAtIndex: index]; @@ -1918,7 +1918,7 @@ - (CECardView *) cardViewForCard: (CECard *) card NSUInteger count, i; // Param check. - require (card, bail); + __Require (card, bail); // Walk through card views looking for the indicated card. count = [_cardViews count]; @@ -1944,7 +1944,7 @@ - (CGRect) boundsForCardAtIndex: (NSUInteger) index forCount: (NSUInteger) count CGRect ourBounds; // Param check. - require (count > 0, bail); + __Require (count > 0, bail); // Get base card bounds. cardBounds.origin = CGPointMake (0.0, 0.0); @@ -2697,7 +2697,7 @@ - (void) animateWithDictionary: (NSDictionary *) dictionary view = [dictionary objectForKey: @"view"]; // Set up animation using the dictionary passed in as a reference. - [UIView beginAnimations: nil context: dictionary]; + [UIView beginAnimations: nil context: (__bridge void *) dictionary]; [UIView setAnimationDelegate: self]; [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; @@ -2731,7 +2731,7 @@ - (void) animationStopped: (NSString *) animationID finished: (NSNumber *) finis _animationRefCount -= 1; // Call handle animation again. - [self handleAnimation: (NSMutableDictionary *) context]; + [self handleAnimation: (__bridge NSMutableDictionary *) context]; } #pragma mark ------ notifications diff --git a/CardEngine/CETableView.m b/CardEngine/CETableView.m index f6e1022..db587e1 100644 --- a/CardEngine/CETableView.m +++ b/CardEngine/CETableView.m @@ -39,7 +39,7 @@ - (id) initForOrientation: (UIInterfaceOrientation) orientation CGRect frame; // Get application frame. - frame = [[UIScreen mainScreen] applicationFrame]; + frame = [[UIScreen mainScreen] bounds]; if (orientation == UIInterfaceOrientationPortrait) { @@ -119,7 +119,7 @@ - (id) initWithFrame: (CGRect) frame // Super. myself = [super initWithFrame: frame]; - require (myself, bail); + __Require (myself, bail); bail: @@ -156,9 +156,17 @@ - (void) drawGradient - (void) drawRect: (CGRect) rect { - UIInterfaceOrientation orientation; - - orientation = [[UIApplication sharedApplication] statusBarOrientation]; + UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait; + + for (UIScene *scene in [[UIApplication sharedApplication] connectedScenes]) + { + if ([scene isKindOfClass: [UIWindowScene class]] && + scene.activationState == UISceneActivationStateForegroundActive) + { + orientation = ((UIWindowScene *) scene).interfaceOrientation; + break; + } + } if (UIInterfaceOrientationIsPortrait (orientation)) { if (_portraitImagePath) diff --git a/Classes/LabSolitaireViewController.m b/Classes/LabSolitaireViewController.m index 732f6f7..f3c9453 100644 --- a/Classes/LabSolitaireViewController.m +++ b/Classes/LabSolitaireViewController.m @@ -1493,7 +1493,7 @@ - (void) _addInfoSubview: (UIView *) subview [self _positionSubviewBottomAndCentered: subview]; // Fade-out the previous view while fading in the new one. - [UIView beginAnimations: @"CrossfadeInfoSubview" context: _aboutView]; + [UIView beginAnimations: @"CrossfadeInfoSubview" context: (__bridge void *) _aboutView]; [UIView setAnimationDelegate: self]; [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; subview.alpha = 1.0; @@ -1725,7 +1725,7 @@ - (void) openGameOverView: (id) sender [self updateLocalStatisticsInterface]; // Animate-out the view sliding out while the dark view becomes clear again. - [UIView beginAnimations: @"CrossfadeInfoSubview" context: _gameOverView]; + [UIView beginAnimations: @"CrossfadeInfoSubview" context: (__bridge void *) _gameOverView]; [UIView setAnimationDelegate: self]; [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; _gameOverView.alpha = 1.0; @@ -1819,7 +1819,7 @@ - (void) animationStopped: (NSString *) animationID finished: (NSNumber *) finis else if ([animationID isEqualToString: @"CrossfadeInfoSubview"]) { [_currentInfoView removeFromSuperview]; - _currentInfoView = context; + _currentInfoView = (__bridge UIView *) context; [_darkView bringSubviewToFront: _currentInfoView]; } } diff --git a/Classes/LocalPlayer.h b/Classes/LocalPlayer.h index f9c62f0..5bc4009 100644 --- a/Classes/LocalPlayer.h +++ b/Classes/LocalPlayer.h @@ -15,7 +15,7 @@ NSString *_alias; BOOL _authenticated; BOOL _usingGameCenter; - id _delegate; + __weak id _delegate; } @property(nonatomic,readonly) NSString *playerID; // Only valid if using Game Center. Must be authenticated. diff --git a/Classes/LocalPlayer.m b/Classes/LocalPlayer.m index 462d264..451b1e2 100644 --- a/Classes/LocalPlayer.m +++ b/Classes/LocalPlayer.m @@ -160,7 +160,7 @@ - (BOOL) retrieveLocalScore: (NSInteger *) score forCategory: (NSString *) categ *score = 0; // Param check. - require (category, bail); + __Require (category, bail); // Get standard user defaults; look for player ID (or 'local') sub-dictionary. defaults = [NSUserDefaults standardUserDefaults]; @@ -194,20 +194,20 @@ - (BOOL) retrieveLocalScore: (NSInteger *) score forCategory: (NSString *) categ - (BOOL) retrieveLeaderboardScores: (NSUInteger) count forCategory: (NSString *) category friendsOnly: (BOOL) friends { GKLeaderboard *leaderboardRequest; - BOOL success = NO; - + // Param checking. - require (count <= 75, bail); - require (category, bail); - + if (count > 75 || category == nil) + return NO; + // We have to have been authenticated and using Game Center. if ((_authenticated == NO) || (_usingGameCenter == NO)) - goto bail; - + return NO; + // Create leaderboard object to request global scores. leaderboardRequest = [[GKLeaderboard alloc] init]; - require (leaderboardRequest, bail); - + if (leaderboardRequest == nil) + return NO; + // Leaderboard attributes. leaderboardRequest.identifier = category; if (friends) @@ -215,12 +215,12 @@ - (BOOL) retrieveLeaderboardScores: (NSUInteger) count forCategory: (NSString *) else leaderboardRequest.playerScope = GKLeaderboardPlayerScopeGlobal; leaderboardRequest.timeScope = GKLeaderboardTimeScopeAllTime; - - // An odd bug, if a player ID comes back as "G: ANONYMOUS", we will fail to get + + // An odd bug, if a player ID comes back as "G: ANONYMOUS", we will fail to get // their scores later (in -[retrieveLeaderboardScoresForPlayerIDs:forCategory:] below). // I'm going to request more than the client asked for and filter out 'anonymous' players. leaderboardRequest.range = NSMakeRange (1, count + 8); - + // Load the scores. [leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) { @@ -262,12 +262,8 @@ - (BOOL) retrieveLeaderboardScores: (NSUInteger) count forCategory: (NSString *) if ([_delegate respondsToSelector: @selector (localPlayer:retrievedLeaderboardScores:playerIDs:forCategory:)]) [_delegate localPlayer: self retrievedLeaderboardScores: values playerIDs: players forCategory: category]; }]; - - success = YES; - -bail: - - return success; + + return YES; } // ------------------------------------------------------------------- retrieveLeaderboardScoresForPlayerIDs:forCategory @@ -275,23 +271,23 @@ - (BOOL) retrieveLeaderboardScores: (NSUInteger) count forCategory: (NSString *) - (BOOL) retrieveLeaderboardScoresForPlayerIDs: (NSArray *) playerIDs forCategory: (NSString *) category { GKLeaderboard *leaderboardRequest; - BOOL success = NO; - + // Param checking. - require (playerIDs, bail); - require (category, bail); - + if (playerIDs == nil || category == nil) + return NO; + // We have to have been authenticated and using Game Center. if ((_authenticated == NO) || (_usingGameCenter == NO)) - goto bail; - + return NO; + // Create leaderboard object to request global scores. leaderboardRequest = [[GKLeaderboard alloc] initWithPlayerIDs: playerIDs]; - require (leaderboardRequest, bail); - + if (leaderboardRequest == nil) + return NO; + // Leaderboard attributes. leaderboardRequest.identifier = category; - + // Load the scores. [leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) { @@ -341,28 +337,19 @@ - (BOOL) retrieveLeaderboardScoresForPlayerIDs: (NSArray *) playerIDs forCategor if ([_delegate respondsToSelector: @selector (localPlayer:retrievedLeaderboardScores:playerIDs:forCategory:)]) [_delegate localPlayer: self retrievedLeaderboardScores: values playerIDs: players forCategory: category]; }]; - - success = YES; - -bail: - - return success; + + return YES; } // ----------------------------------------------------------------------------------------- retrieveAliasesForPlayerIDs - (BOOL) retrieveAliasesForPlayerIDs: (NSArray *) playerIDs { - BOOL success = NO; - // We have to have been authenticated and using Game Center. if ((_authenticated == NO) || (_usingGameCenter == NO)) - goto bail; - - success = YES; - - [GKPlayer loadPlayersForIdentifiers: playerIDs withCompletionHandler: ^(NSArray *players, NSError *error) - { + return NO; + + [GKPlayer loadPlayersForIdentifiers: playerIDs withCompletionHandler: ^(NSArray *players, NSError *error) { NSMutableArray *aliases = nil; if (error != nil) @@ -402,10 +389,8 @@ - (BOOL) retrieveAliasesForPlayerIDs: (NSArray *) playerIDs if ([_delegate respondsToSelector: @selector (localPlayer:retrievedAliasesForPlayerIDs:)]) [_delegate localPlayer: self retrievedAliasesForPlayerIDs: aliases]; }]; - -bail: - - return success; + + return YES; } // ------------------------------------------------------------------- retrieveLeaderboardScoreForLocalPlayerForCategory @@ -413,22 +398,23 @@ - (BOOL) retrieveAliasesForPlayerIDs: (NSArray *) playerIDs - (BOOL) retrieveLeaderboardScoreForLocalPlayerForCategory: (NSString *) category { GKLeaderboard *leaderboardRequest; - BOOL success = NO; - + // Param checking. - require (category, bail); - + if (category == nil) + return NO; + // We have to have been authenticated and using Game Center. if ((_authenticated == NO) || (_usingGameCenter == NO)) - goto bail; - + return NO; + // Create leaderboard object to request global scores. leaderboardRequest = [[GKLeaderboard alloc] initWithPlayerIDs: [NSArray arrayWithObject: _playerID]]; - require (leaderboardRequest, bail); - + if (leaderboardRequest == nil) + return NO; + // Leaderboard attributes. leaderboardRequest.identifier = category; - + // Load the scores. [leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) { @@ -450,12 +436,8 @@ - (BOOL) retrieveLeaderboardScoreForLocalPlayerForCategory: (NSString *) categor [_delegate localPlayer: self retrievedLeaderboardScoreForLocalPlayer: oneScore.value forCategory: category]; } }]; - - success = YES; - -bail: - - return success; + + return YES; } @end From 4d1c3fbbf450fbbc0e2040dd6ad6a53ea192e6de Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2026 21:44:41 +0200 Subject: [PATCH 09/33] Modernize app startup: migrate to UIScene-based lifecycle - Add LabSolitaireSceneDelegate adopting UIWindowSceneDelegate - Make LabSolitaireAppDelegate inherit from UIResponder; remove window/viewController properties (now scene-owned) - Move window setup, root view controller, splash, and state save/restore from AppDelegate to SceneDelegate - Replace NSMainNibFile in Info.plist with UIApplicationSceneManifest declaring LabSolitaireSceneDelegate as the scene delegate - Use NSStringFromClass in main.m for type-safe delegate reference - Remove unused MainWindow.xib and placeholder LabSolitaire.storyboard from project (files retained on disk for now) App now launches into the splash screen and accepts touch events. --- Classes/LabSolitaireAppDelegate.h | 12 +------ Classes/LabSolitaireAppDelegate.m | 45 +------------------------- Classes/LabSolitaireSceneDelegate.h | 13 ++++++++ Classes/LabSolitaireSceneDelegate.m | 39 ++++++++++++++++++++++ LabSolitaire-Info.plist | 19 +++++++++-- LabSolitaire.xcodeproj/project.pbxproj | 14 ++++---- main.m | 3 +- 7 files changed, 79 insertions(+), 66 deletions(-) create mode 100644 Classes/LabSolitaireSceneDelegate.h create mode 100644 Classes/LabSolitaireSceneDelegate.m diff --git a/Classes/LabSolitaireAppDelegate.h b/Classes/LabSolitaireAppDelegate.h index eaf61b4..b67898e 100644 --- a/Classes/LabSolitaireAppDelegate.h +++ b/Classes/LabSolitaireAppDelegate.h @@ -6,17 +6,7 @@ #import -@class LabSolitaireViewController; - - -@interface LabSolitaireAppDelegate : NSObject -{ - UIWindow *_window; - LabSolitaireViewController *_viewController; -} - -@property (nonatomic, strong) IBOutlet UIWindow *_window; -@property (nonatomic, strong) IBOutlet LabSolitaireViewController *_viewController; +@interface LabSolitaireAppDelegate : UIResponder @end diff --git a/Classes/LabSolitaireAppDelegate.m b/Classes/LabSolitaireAppDelegate.m index dc72c92..0a4e0e9 100644 --- a/Classes/LabSolitaireAppDelegate.m +++ b/Classes/LabSolitaireAppDelegate.m @@ -4,58 +4,15 @@ #import "LabSolitaireAppDelegate.h" -#import "LabSolitaireViewController.h" @implementation LabSolitaireAppDelegate // ============================================================================================= LabSolitaireAppDelegate -// ---------------------------------------------------------------------------------------------------------- synthesize - -@synthesize _window; -@synthesize _viewController; - // --------------------------------------------------------------------------- application:didFinishLaunchingWithOptions - (BOOL) application: (UIApplication *) application didFinishLaunchingWithOptions: (NSDictionary *) launchOptions -{ - // Override point for customization after app launch. - [_window setRootViewController: _viewController]; - [_window addSubview: _viewController.view]; - [_window makeKeyAndVisible]; - - // Create stacks. - [_viewController createCardTableLayout]; - - // Restore card layout. - [_viewController restoreState]; - - // Display splash (info) view. - [_viewController openSplashAfterDelay]; - - return YES; -} - -// --------------------------------------------------------------------------------------- applicationDidEnterBackground - -- (void) applicationDidEnterBackground: (UIApplication *) application { - // Store away game state. - [_viewController saveState]; -} - -// -------------------------------------------------------------------------------------------- applicationWillTerminate - -- (void) applicationWillTerminate: (UIApplication *) application -{ - // Store away game state. - [_viewController saveState]; -} - -// ---------------------------------------------------------------------------------- applicationDidReceiveMemoryWarning - -- (void) applicationDidReceiveMemoryWarning: (UIApplication *) application -{ - printf ("applicationDidReceiveMemoryWarning\n"); + return YES; } @end diff --git a/Classes/LabSolitaireSceneDelegate.h b/Classes/LabSolitaireSceneDelegate.h new file mode 100644 index 0000000..a933960 --- /dev/null +++ b/Classes/LabSolitaireSceneDelegate.h @@ -0,0 +1,13 @@ +// ===================================================================================================================== +// LabSolitaireSceneDelegate.h +// ===================================================================================================================== + + +#import + + +@interface LabSolitaireSceneDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/Classes/LabSolitaireSceneDelegate.m b/Classes/LabSolitaireSceneDelegate.m new file mode 100644 index 0000000..314b516 --- /dev/null +++ b/Classes/LabSolitaireSceneDelegate.m @@ -0,0 +1,39 @@ +// ===================================================================================================================== +// LabSolitaireSceneDelegate.m +// ===================================================================================================================== + + +#import "LabSolitaireSceneDelegate.h" +#import "LabSolitaireViewController.h" + + +@implementation LabSolitaireSceneDelegate +{ + LabSolitaireViewController *_viewController; +} + +// -------------------------------------------------------------------------------------------------------- scene:willConnectToSession:options + +- (void) scene: (UIScene *) scene willConnectToSession: (UISceneSession *) session options: (UISceneConnectionOptions *) connectionOptions +{ + UIWindowScene *windowScene = (UIWindowScene *) scene; + + _viewController = [[LabSolitaireViewController alloc] initWithNibName: @"LabSolitaireViewController" bundle: nil]; + + self.window = [[UIWindow alloc] initWithWindowScene: windowScene]; + self.window.rootViewController = _viewController; + [self.window makeKeyAndVisible]; + + [_viewController createCardTableLayout]; + [_viewController restoreState]; + [_viewController openSplashAfterDelay]; +} + +// -------------------------------------------------------------------------------------------------------- sceneDidEnterBackground + +- (void) sceneDidEnterBackground: (UIScene *) scene +{ + [_viewController saveState]; +} + +@end diff --git a/LabSolitaire-Info.plist b/LabSolitaire-Info.plist index 8e669af..f0c1165 100644 --- a/LabSolitaire-Info.plist +++ b/LabSolitaire-Info.plist @@ -50,8 +50,23 @@ 1.1.0 LSRequiresIPhoneOS - NSMainNibFile - MainWindow + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + LabSolitaireSceneDelegate + + + + UIPrerenderedIcon UISupportedInterfaceOrientations diff --git a/LabSolitaire.xcodeproj/project.pbxproj b/LabSolitaire.xcodeproj/project.pbxproj index f2988f0..6cd6258 100755 --- a/LabSolitaire.xcodeproj/project.pbxproj +++ b/LabSolitaire.xcodeproj/project.pbxproj @@ -75,7 +75,6 @@ 265BB96C14EF6E3200B5D3D4 /* TablePortrait@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 265BB96A14EF6E3200B5D3D4 /* TablePortrait@2x.png */; }; 265BB96E14EF6E3D00B5D3D4 /* GliderAd@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 265BB96D14EF6E3D00B5D3D4 /* GliderAd@2x.png */; }; 265FA4F613DE86E60067ED18 /* CardShadow.png in Resources */ = {isa = PBXBuildFile; fileRef = 265FA4F513DE86E60067ED18 /* CardShadow.png */; }; - 2660DCEE18A210C00023B6DA /* LabSolitaire.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2660DCED18A210C00023B6DA /* LabSolitaire.storyboard */; }; 26640E0F1320114900DA94E4 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26640E0E1320114900DA94E4 /* AVFoundation.framework */; }; 2677A478142BF20900022E23 /* ParlourSolitaireAd.png in Resources */ = {isa = PBXBuildFile; fileRef = 2677A477142BF20900022E23 /* ParlourSolitaireAd.png */; }; 268DE47813230B5300C68035 /* Blip.wav in Resources */ = {isa = PBXBuildFile; fileRef = 268DE47713230B5300C68035 /* Blip.wav */; }; @@ -209,9 +208,9 @@ 26FF236B1303042D005E20CB /* 12D.png in Resources */ = {isa = PBXBuildFile; fileRef = 26FF23671303042D005E20CB /* 12D.png */; }; 26FF236C1303042D005E20CB /* 13D.png in Resources */ = {isa = PBXBuildFile; fileRef = 26FF23681303042D005E20CB /* 13D.png */; }; 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; }; - 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; }; 28D7ACF80DDB3853001CB0EB /* LabSolitaireViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* LabSolitaireViewController.m */; }; 4C1616F72FA61289002DEE82 /* LSAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */; }; + 4C1616FA2FA67749002DEE82 /* LabSolitaireSceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1616F92FA67749002DEE82 /* LabSolitaireSceneDelegate.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -285,7 +284,6 @@ 265BB96A14EF6E3200B5D3D4 /* TablePortrait@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "TablePortrait@2x.png"; sourceTree = ""; }; 265BB96D14EF6E3D00B5D3D4 /* GliderAd@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "GliderAd@2x.png"; sourceTree = ""; }; 265FA4F513DE86E60067ED18 /* CardShadow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = CardShadow.png; sourceTree = ""; }; - 2660DCED18A210C00023B6DA /* LabSolitaire.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LabSolitaire.storyboard; sourceTree = ""; }; 26640E0E1320114900DA94E4 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 2677A477142BF20900022E23 /* ParlourSolitaireAd.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ParlourSolitaireAd.png; sourceTree = ""; }; 268DE47713230B5300C68035 /* Blip.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = Blip.wav; sourceTree = ""; }; @@ -436,12 +434,13 @@ 26FF23671303042D005E20CB /* 12D.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 12D.png; sourceTree = ""; }; 26FF23681303042D005E20CB /* 13D.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 13D.png; sourceTree = ""; }; 288765A40DF7441C002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - 28AD733E0D9D9553002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 28D7ACF60DDB3853001CB0EB /* LabSolitaireViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabSolitaireViewController.h; sourceTree = ""; }; 28D7ACF70DDB3853001CB0EB /* LabSolitaireViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LabSolitaireViewController.m; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 4C1616F52FA61289002DEE82 /* LSAudioEngine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LSAudioEngine.h; sourceTree = ""; }; 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LSAudioEngine.m; sourceTree = ""; }; + 4C1616F82FA67749002DEE82 /* LabSolitaireSceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LabSolitaireSceneDelegate.h; sourceTree = ""; }; + 4C1616F92FA67749002DEE82 /* LabSolitaireSceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LabSolitaireSceneDelegate.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -465,6 +464,8 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( + 4C1616F82FA67749002DEE82 /* LabSolitaireSceneDelegate.h */, + 4C1616F92FA67749002DEE82 /* LabSolitaireSceneDelegate.m */, 4C1616F52FA61289002DEE82 /* LSAudioEngine.h */, 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */, 26ED7011143CD2BA00E06F05 /* LocalPlayer.h */, @@ -738,9 +739,7 @@ 26DE150F12F4A0C600FF88C6 /* Art */, 26640E78132012C100DA94E4 /* Sounds */, 26C97423140E84B400D2C373 /* Localizable.strings */, - 2660DCED18A210C00023B6DA /* LabSolitaire.storyboard */, 2641ED80140818FD00709D17 /* LabSolitaireViewController.xib */, - 28AD733E0D9D9553002E5188 /* MainWindow.xib */, 26ABC07013C5A0D600ABCE84 /* LabSolitaire-Info.plist */, 26B1E41C131D6E6900DFA4EA /* Icon-72.png */, 26A292D81509BE49004F24F2 /* Icon-144.png */, @@ -826,7 +825,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */, 26DE151212F4A0C600FF88C6 /* TableLandscape.png in Resources */, 26DE151312F4A0C600FF88C6 /* TablePortrait.png in Resources */, 26C84EC91301027500EB4D52 /* 1S.png in Resources */, @@ -867,7 +865,6 @@ 26FF23341302F068005E20CB /* 10C.png in Resources */, 26FF23451302FDCA005E20CB /* 11C.png in Resources */, 26FF23461302FDCA005E20CB /* 12C.png in Resources */, - 2660DCEE18A210C00023B6DA /* LabSolitaire.storyboard in Resources */, 26FF23471302FDCA005E20CB /* 13C.png in Resources */, 26FF234B1302FDDA005E20CB /* 1D.png in Resources */, 26FF234C1302FDDA005E20CB /* 2D.png in Resources */, @@ -1025,6 +1022,7 @@ 1D3623260D0F684500981E51 /* LabSolitaireAppDelegate.m in Sources */, 28D7ACF80DDB3853001CB0EB /* LabSolitaireViewController.m in Sources */, 26DE146912F4902B00FF88C6 /* CECard.m in Sources */, + 4C1616FA2FA67749002DEE82 /* LabSolitaireSceneDelegate.m in Sources */, 4C1616F72FA61289002DEE82 /* LSAudioEngine.m in Sources */, 26DE146A12F4902B00FF88C6 /* CECardView.m in Sources */, 26DE146B12F4902B00FF88C6 /* CEStack.m in Sources */, diff --git a/main.m b/main.m index 9e2287d..0b88738 100644 --- a/main.m +++ b/main.m @@ -4,13 +4,14 @@ #import +#import "LabSolitaireAppDelegate.h" int main (int argc, char *argv[]) { @autoreleasepool { - int retVal = UIApplicationMain (argc, argv, nil, @"LabSolitaireAppDelegate"); + int retVal = UIApplicationMain (argc, argv, nil, NSStringFromClass ([LabSolitaireAppDelegate class])); return retVal; } } From 80ff5969848edf493359e71e87f2909d49181d61 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2026 22:49:47 +0200 Subject: [PATCH 10/33] Fix info view tab switching and close behavior - Guard info: against re-entry when info view is already open; delegate to _addInfoSubview: for tab switch instead of full re-open - Fix _addInfoSubview: animation context to track the actual subview, so _currentInfoView reflects the visible tab and close dismisses the right view --- Classes/LabSolitaireViewController.h | 1 - Classes/LabSolitaireViewController.m | 15 ++++++++------- en.lproj/LabSolitaireViewController.xib | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Classes/LabSolitaireViewController.h b/Classes/LabSolitaireViewController.h index 2f6925c..43b6837 100644 --- a/Classes/LabSolitaireViewController.h +++ b/Classes/LabSolitaireViewController.h @@ -53,7 +53,6 @@ UIView *_darkView; UIView *_infoView; UIView *_overlayingView; // Weak reference. - IBOutlet UIViewController *_aboutViewController; IBOutlet UIView *_aboutView; IBOutlet UIView *_settingsView; IBOutlet UIView *_rulesView; diff --git a/Classes/LabSolitaireViewController.m b/Classes/LabSolitaireViewController.m index f3c9453..1f612ee 100644 --- a/Classes/LabSolitaireViewController.m +++ b/Classes/LabSolitaireViewController.m @@ -1292,11 +1292,17 @@ - (void) updateGlobalScoresInterface - (void) info: (id) sender { + if (_infoViewIsOpen) + { + [self _addInfoSubview: _aboutView]; + return; + } + if (_playSounds) { [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } - + _infoViewIsOpen = YES; _wasAutoPutaway = _autoPutaway; _wasAutoPutawayMode = _autoPutawayMode; @@ -1313,9 +1319,6 @@ - (void) info: (id) sender [self updateGlobalScoresInterface]; } - [_aboutViewController setModalPresentationStyle: UIModalPresentationOverCurrentContext]; - [self presentViewController: _aboutViewController animated: YES completion: nil]; - // Initially begin with "about view" being displayed. _currentInfoView = _aboutView; @@ -1493,7 +1496,7 @@ - (void) _addInfoSubview: (UIView *) subview [self _positionSubviewBottomAndCentered: subview]; // Fade-out the previous view while fading in the new one. - [UIView beginAnimations: @"CrossfadeInfoSubview" context: (__bridge void *) _aboutView]; + [UIView beginAnimations: @"CrossfadeInfoSubview" context: (__bridge void *) subview]; [UIView setAnimationDelegate: self]; [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; subview.alpha = 1.0; @@ -1538,8 +1541,6 @@ - (void) closeInfo: (id) sender [[LSAudioEngine sharedEngine] playEffect: @"ClickClose.wav"]; } - [_aboutViewController dismissViewControllerAnimated: YES completion: nil]; - mainBounds = [[UIScreen mainScreen] bounds]; portrait = UIInterfaceOrientationIsPortrait ([UIApplication sharedApplication].statusBarOrientation); diff --git a/en.lproj/LabSolitaireViewController.xib b/en.lproj/LabSolitaireViewController.xib index 80c0e20..0c44fe9 100644 --- a/en.lproj/LabSolitaireViewController.xib +++ b/en.lproj/LabSolitaireViewController.xib @@ -12,7 +12,6 @@ - From c0574dad5458c096e2023fa694f7bed9df90a14a Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2026 23:21:25 +0200 Subject: [PATCH 11/33] Fix card animation memory ownership across UIView animation context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card-move and card-flip animations in CEStackView passed an NSMutableDictionary holding all animation parameters through beginAnimations:context: as a void pointer, retrieving it in the animationStopped:finished:context: callback. Both casts used plain __bridge, which under ARC does not transfer ownership. The dictionary was a local object — once the originating method returned, ARC released it, and by the time the animation completed the void* context pointed to recycled memory (manifesting as a phantom UIImageConfiguration receiving objectForKey:). This crashed on every double-tap auto-move and on auto-putaway. - Line 2700: __bridge -> __bridge_retained when passing to context - Line 2734: __bridge -> __bridge_transfer when retrieving from context The pair is balanced: __bridge_retained adds a +1 retain that survives the animation; __bridge_transfer hands ownership back to ARC, which releases the dictionary normally when the local goes out of scope. Audited the remaining __bridge casts in LabSolitaireViewController.m; they pass strong IBOutlets (_aboutView, _settingsView, _rulesView, _gameOverView) which are guaranteed alive for the animation lifetime, so plain __bridge is correct there. The game now plays end-to-end: drag-and-drop, double-tap auto-move, auto-putaway, and multi-card moves all work. --- CardEngine/CEStackView.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CardEngine/CEStackView.m b/CardEngine/CEStackView.m index af8686e..09df68c 100644 --- a/CardEngine/CEStackView.m +++ b/CardEngine/CEStackView.m @@ -2697,7 +2697,7 @@ - (void) animateWithDictionary: (NSDictionary *) dictionary view = [dictionary objectForKey: @"view"]; // Set up animation using the dictionary passed in as a reference. - [UIView beginAnimations: nil context: (__bridge void *) dictionary]; + [UIView beginAnimations: nil context: (__bridge_retained void *) dictionary]; [UIView setAnimationDelegate: self]; [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; @@ -2731,7 +2731,7 @@ - (void) animationStopped: (NSString *) animationID finished: (NSNumber *) finis _animationRefCount -= 1; // Call handle animation again. - [self handleAnimation: (__bridge NSMutableDictionary *) context]; + [self handleAnimation: (__bridge_transfer NSMutableDictionary *) context]; } #pragma mark ------ notifications From 8d3f31935b5deaf63f5e69bb46ee3df90fd4a961 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 3 May 2026 00:02:57 +0200 Subject: [PATCH 12/33] Updated MARKETING_VERSION to 1.1.1 --- LabSolitaire-Info.plist | 4 ++-- LabSolitaire.xcodeproj/project.pbxproj | 4 ++++ en.lproj/LabSolitaireViewController.xib | 2 +- ja.lproj/LabSolitaireViewController.xib | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/LabSolitaire-Info.plist b/LabSolitaire-Info.plist index f0c1165..5b67de8 100644 --- a/LabSolitaire-Info.plist +++ b/LabSolitaire-Info.plist @@ -41,13 +41,13 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.0 + 1.1.1 CFBundleSignature ???? CFBundleURLTypes CFBundleVersion - 1.1.0 + 1.1.1 LSRequiresIPhoneOS UIApplicationSceneManifest diff --git a/LabSolitaire.xcodeproj/project.pbxproj b/LabSolitaire.xcodeproj/project.pbxproj index 6cd6258..97ca4c7 100755 --- a/LabSolitaire.xcodeproj/project.pbxproj +++ b/LabSolitaire.xcodeproj/project.pbxproj @@ -441,6 +441,7 @@ 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LSAudioEngine.m; sourceTree = ""; }; 4C1616F82FA67749002DEE82 /* LabSolitaireSceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LabSolitaireSceneDelegate.h; sourceTree = ""; }; 4C1616F92FA67749002DEE82 /* LabSolitaireSceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LabSolitaireSceneDelegate.m; sourceTree = ""; }; + 4C85D9DC2FA6A947006E136A /* ja */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ja; path = ja.lproj/LabSolitaireViewController.xib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1042,6 +1043,7 @@ isa = PBXVariantGroup; children = ( 2641ED7F140818FD00709D17 /* en */, + 4C85D9DC2FA6A947006E136A /* ja */, ); name = LabSolitaireViewController.xib; sourceTree = ""; @@ -1074,6 +1076,7 @@ INFOPLIST_FILE = "LabSolitaire-Info.plist"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.card-games"; IPHONEOS_DEPLOYMENT_TARGET = 17.6; + MARKETING_VERSION = 1.1.1; PRODUCT_BUNDLE_IDENTIFIER = com.softdorothy.labsolitaire; PRODUCT_NAME = "Lab Solitaire"; PROVISIONING_PROFILE = ""; @@ -1093,6 +1096,7 @@ INFOPLIST_FILE = "LabSolitaire-Info.plist"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.card-games"; IPHONEOS_DEPLOYMENT_TARGET = 17.6; + MARKETING_VERSION = 1.1.1; PRODUCT_BUNDLE_IDENTIFIER = com.softdorothy.labsolitaire; PRODUCT_NAME = "Lab Solitaire"; PROVISIONING_PROFILE = ""; diff --git a/en.lproj/LabSolitaireViewController.xib b/en.lproj/LabSolitaireViewController.xib index 0c44fe9..b7365ba 100644 --- a/en.lproj/LabSolitaireViewController.xib +++ b/en.lproj/LabSolitaireViewController.xib @@ -659,7 +659,7 @@ Player -