使用window-based模板创建一个单view程序

本文详细介绍如何使用window-based模板创建iOS应用程序,并逐步解析视图控制器、XIB文件的创建及关联过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

去我的目录

上一篇我们使用iPhone自带的view-based模板创建了第一个程序HelloWorld。但是在实际工作中,我们最常使用的还是window-based模板,任何一个iPhone程序都可以基于这个模板构建,因为它是最通用的模板。废话不多说,我们开始吧。

 

这次我们使用window-based模板来创建上一篇中的HelloWorld。

<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>

启动xCode,File->New Project->选择window-based模板,给工程取名为HelloWorld。首先来看一看模板都提供了哪些默认的文件。

workspace prev_workspace

上图分别是本工程和上一个工程的workspace的截图。可以看到,如果使用window-based模板,在Classes文件夹中少了HelloWorldViewController,在Resources文件夹中少了HelloWorldViewController.xib。这是因为view-based模板给我们提供了所需的view,而window-based模板则把这个任务留给了开发者。

我们再来看一看本工程的HelloWorldAppDelegate.h和.m文件,跟上一篇中的HelloWorldAppDelegate相比,少了

1)HelloWorldViewController* viewController 的声明,

2)和添加view的语句:[window addSubview:viewController.view];

这也是我们需要做的工作。

 

下面我们来添加一个新的view。

选中Classes文件夹,右键,选择Add->New File->选择iPhone OS下的Cocoa Touch Classes,然后在右边的菜单中选择UIViewController subclass,取名为HelloWorldViewController。(注意:记得选上“Also create "HelloWorldViewController.h"”)这样,我们就添加了控制这个新view的类-HelloWorldViewController。

new file

我们还需要创建一个.xib文件,通过这个xib文件,可以编辑view的UI。选中Resources文件夹,右键,选择Add->New File->选择iPhone OS下的User Interfaces,然后在右边的菜单中选择View XIB,然后取名为HelloWorldViewController。

new UI

这样,我们就完成了新view的添加。可以这么理解:.xib文件是这个view的表现层(用户所看到的),而.h和.m文件则是这个view的控制(负责处理用户交互的)。如果我们还记得上一篇提到过的MVC结构,那么.xib就是V,而.h和.m就对应这C。

 

 

下面我们来添加代码和控件。

首先,在HelloWorldAppDelegate.h中:

#import <UIKit/UIKit.h>
@class HelloWorldViewController;
@interface HelloWorldAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    HelloWorldViewController *viewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) HelloWorldViewController *viewController;
@end


这里在HelloWorldAppDelegate中添加了一个新的成员,就是我们刚添加的view的controller。注意,我们还添加了一句声明:@property (nonatomic, retain) HelloWorldViewController *viewController;

 

Objective-C中的关键词@property允许我们设置变量的属性。它的具体含义是什么呢?下面来详细解释一下:

C++ 中,我们通过.运算符来访问成员变量,如aClass.memberVar = 1; 但是在Objective-C中,我们必须实现setter和getter函数才能访问和修改成员变量(如void setMemberVar(int i)和int getMemberVar() )。为了简化编码,Objective-C提供了关键词@property,它告诉编译器为我们生成getter和setter函数。在@property后面,紧跟着一些属性,编译器根据这些属性,为getter和setter生成不同的代码。

1)nonatomic: 在默认情况下,编译器生成getter和setter函数的时候,会添加一些其他代码(主要是考虑到多线程的程序)。这里,我们不需要这些额外的代码,关键词nonatomic就是告诉编译器不要添加这些代码。

2)retain: 在默认情况下,编译器以assign的方式生成setter。而retain则告诉编译器,在生成setter函数的时候,需要调用setter参数的retain方法。如:

         *ASSIGN 方式*                                                       *RETAIN 方式*

- (void) setString: (NSString*) str {                        - (void) setString: (NSString*) str {

            theString = str;                                                     [str retain];

            ... ...                                                                    [theString release];

                                                                                       theString = str; ... ...

}                                                                          }

 

在HelloWorldAppDelegate.m中,添加:

@synthesize window;
@synthesize viewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {   
    HelloWorldViewController *ctrl =

      [[HelloWorldViewController alloc] initWithNibName:@"HelloWorldViewController"
                                                                                bundle:[NSBundle mainBundle]];
    self.viewController = ctrl;
    [ctrl release];
    [window addSubview:[self.viewController view]];

    // Override point for customization after application launch
    [window makeKeyAndVisible];
}
- (void)dealloc {
    [viewController release];
    [window release];
    [super dealloc];
}

1)@synthesize是真正的生成setter和getter函数的命令。在头文件中出现的@property只是设置属性的命令。

2)程序启动后,会自动装载MainWindow.xib,但是它并不知道要装载HelloWorldViewController.xib。这是我们添加的资源文件,所以在程序装载完MainWindow.xib之后,需要添加代码,让程序来装载HelloWorldViewController.xib。UIViewController中有一个成员变量view,这里HelloWorldViewController继承了UIViewController,自然也有这个成员变量。成员变量view对应于用户看到的UI(这里对应的UI是什么?见下文)。所以,需要将HelloWorldViewController的view添加到window中。

[window addSubview:[self.viewController view]];

3)由于viewController的属性是retain,所以在dealloc中,需要调用release释放资源。

 

下面我们来编辑这个新view所对应的UI。双击HelloWorldViewController.xib,启动Interface Builder。从控件库中添加UILabel和UIButton。调整控件的大小和位置。保存。

 

选择File's Owner,然后组合键cmd+4,在Class Identity中输入HelloWorldViewController。这是告诉Interface Builder将这个资源与HelloWorldViewController这个类联系起来。这样,我们就能够通过Interface Builder将这个资源中的控件与HelloWorldViewController类中的代码联系起来。

 

在HelloWorldViewController.h中,添加:
@interface HelloWorldViewController : UIViewController {
    IBOutlet UILabel * m_label;
}
@property (nonatomic, retain) IBOutlet UILabel * m_label;
- (IBAction) onClick;

@end

由于我们需要修改UILabel中的文字,所以需要在代码声明这个UILabel,然后利用Interface Builder将这个变量和控件联系起来。同时,我们还声明了函数onClick,用来处理按键事件。同样,我们也要用Interface Builder将这个函数和控件事件联系起来。

 

在HelloWorldViewController.m中,添加:

@implementation HelloWorldViewController
@synthesize m_label;

... ...

- (IBAction) onClick {
    m_label.text = @"Hello World!";
}

- (void)dealloc {
    [m_label release];
    [super dealloc];
}

 


然后就是用Interface Builder将它们和控件联系起来:

选中File's Owner,按住ctrl,点击鼠标左键并拖拽鼠标至UILabel上方,释放左键,选择m_label。这样就将变量和控件联系起来了。

connect label

 

选中控件UIButton,组合键cmd+2,选择Touch Up Inside,将鼠标移至Touch Up Inside右边的圆圈上(变成×),然后拖拽鼠标至File's Owner,释放左键,选择onClick。这样就完成了鼠标事件和函数的连接。

connect action

 

最后一步:上面我们提到过,UIViewController有一个成员变量view,这个view有对应的UI。很显然这里所谓的对应的UI,就是刚才我们使用Interface Builder编辑过的控件View(我们之前用Interface Builder添加了UIButton和UILabel到控件View中;在HelloWorldViewController.xib中,我们看到View旁边是一个小箭头,点击展开,下面有UILabel和UIButton),现在我们需要将UIViewController这个类中的成员变量view和控件View联系起来。这样,在运行代码:[window addSubview:[self.viewController view]]; 的时候,程序才知道需要装载哪个view。

选中File's Owner,组合键cmd+2,在Outlets下面,可以看到view现在没有和任何控件联系起来。将鼠标移至view右边的圆圈上(变成×),然后按住左键并拖拽鼠标至HelloWorldViewController.xib中的View上方,然后释放鼠标。这样,我们就完成了连接。

(上文中出现了两个不同的view,一个是UIViewController的成员变量view,另一个是控件View)

connect view

 

编译,运行!我们就得到了和上一篇一样的Hello World!

<think>好的,我现在需要帮助用户在WPF中使用XAML实现一个按钮控制两套界面的切换。根据用户之前的对话,他们可能已经对快速排序和SQL占位符替换有一定的了解,但现在的问题转向了WPF界面切换。让我先理清楚用户的需求。 用户想要一个按钮来控制两套界面的切换,也就是点击按钮时,主界面在两个不同的视图之间切换。我需要考虑如何用XAML和C#来实现这个功能。根据提供的引用内容,用户可能已经接触过资源字典、Prism框架和Frame导航。不过用户这次没有提到使用Prism,可能更倾向于原生WPF的实现。 首先,我需要回忆WPF中的界面切换方法。常见的做法有使用Frame控件结合Page页面,或者使用ContentControl动态切换UserControl。引用[3]提到了使用Frame和ListBox进行导航,这可能是一个相关的方法。用户希望用按钮来控制切换,所以可能需要将ListBox替换为按钮的点击事件。 接下来,考虑数据绑定和命令。如果用户希望更符合MVVM模式,可能需要使用Prism,但根据用户当前的问题,可能更倾向于简的实现,不需要复杂的框架。不过引用[2]提到了Prism的配置,但用户现在的问题可能不需要用到Prism,除非他们已经在项目中使用了它。但用户的问题中没有明确说明,所以暂时假设使用原生WPF。 然后,我需要确定如何存储两套界面。可能的方案是将两个不同的UserControl作为资源,或者使用不同的Page,然后通过按钮切换Frame的Source属性。或者使用Visibility属性来控制哪个UserControl显示。 另一个可能性是使用DataTemplate和ContentControl,通过改变Content属性的值来切换视图。例如,定义两个DataTemplate,分别对应两个视图,然后通过按钮切换绑定的ViewModel属性,从而触发模板切换。不过这可能涉及到MVVM模式,需要更多的设置。 或者,使用视图模型中的属性来表示当前显示的视图,然后通过绑定到ContentControl的Content属性,结合DataTemplate选择不同的视图。这种方法需要数据绑定和可能的转换器,但比较符合WPF的设计模式。 不过用户可能希望一个更简的实现,比如在按钮的Click事件中直接切换Frame的导航。例如,点击按钮时,Frame导航到Page1或Page2。这种方法不需要复杂的绑定,适合快速实现。 根据引用[3],用户之前可能使用过Frame和ListBox进行页面跳转,但这次希望用按钮代替ListBox。因此,可以将ListBox的SelectionChanged事件改为按钮的Click事件来触发导航。 我需要考虑如何将两个不同的页面(Page1和Page2)嵌入到主窗口,并通过按钮切换它们的显示。可能的XAML结构包括一个Frame控件,两个Page,以及一个按钮,点击时切换Frame的Source或Content。 另外,用户可能需要两套“界面”,这可能是指两个不同的UserControl,或者两个不同的布局。使用Frame和Page可能更合适,因为Page支持导航历史,但用户可能不需要历史记录,只需要切换。因此,可以考虑直接切换UserControl的Visibility,或者使用ContentControl来切换不同的内容。 总结可能的步骤: 1. 创建两个UserControl或Page,分别代表两套界面。 2. 在主窗口中放置一个ContentControl或Frame来承载当前显示的界面。 3. 使用按钮的Click事件来切换当前显示的界面。 4. 可能需要使用布尔变量或枚举来跟踪当前显示的界面,并在事件处理中更新内容。 考虑到用户可能希望XAML和代码分离,使用事件处理可能更直接。但如果用户熟悉数据绑定,可以尝试MVVM方式,将当前视图绑定到ViewModel的属性,通过命令切换。 现在需要确定具体的实现方式。假设用户希望尽可能简的代码,使用后台代码处理按钮点击,切换UserControl的显示。例如: 主窗口的XAML中有一个按钮和一个ContentControl: ```xaml <Button Content="切换界面" Click="ToggleButton_Click"/> <ContentControl x:Name="MainContent"/> ``` 后台代码: ```csharp private UserControl currentView; private UserControl view1 = new View1(); private UserControl view2 = new View2(); public MainWindow() { InitializeComponent(); MainContent.Content = view1; currentView = view1; } private void ToggleButton_Click(object sender, RoutedEventArgs e) { if (currentView == view1) { MainContent.Content = view2; currentView = view2; } else { MainContent.Content = view1; currentView = view1; } } ``` 这种方法简,但需要后台代码。另一种方法是使用DataTrigger和视图模型,但可能更复杂。 另外,引用[1]中提到了样式设置,用户可能需要按钮的样式,比如背景色和前景色,可以应用类似的样式到切换按钮。 还需要考虑如何组织XAML,可能需要将两个UserControl定义在资源字典中,或者在主窗口中直接引用。 综合以上分析,用户可能需要一个包含按钮和内容区域的布局,点击按钮切换内容区域显示不同的用户控件。接下来,我需要将这一思路转化为具体的XAML和C#代码,并确保解释清晰,步骤正确。</think>### WPF XAML按钮切换双界面完整实现方案 #### 方法一:UserControl+Visibility切换(适合简场景) ```xml <!-- MainWindow.xaml --> <Window x:Class="SwitchDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:local="clr-namespace:SwitchDemo" Title="界面切换" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> <!-- 切换按钮 --> <Button Content="切换界面" Click="ToggleView" Padding="15,5" Margin="10" FontSize="16" Background="#FF2196F3" Foreground="White"/> <!-- 界面容器 --> <Grid Grid.Row="1"> <local:ViewA x:Name="viewA" Visibility="Visible"/> <local:ViewB x:Name="viewB" Visibility="Collapsed"/> </Grid> </Grid> </Window> ``` ```csharp // MainWindow.xaml.cs private void ToggleView(object sender, RoutedEventArgs e) { viewA.Visibility = viewA.IsVisible ? Visibility.Collapsed : Visibility.Visible; viewB.Visibility = viewB.IsVisible ? Visibility.Collapsed : Visibility.Visible; } ``` #### 方法二:ContentControl+DataTemplate(MVVM模式推荐) ```xml <!-- 主窗口 --> <Window.Resources> <DataTemplate DataType="{x:Type local:ViewModelA}"> <local:ViewA/> </DataTemplate> <DataTemplate DataType="{x:Type local:ViewModelB}"> <local:ViewB/> </DataTemplate> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> <Button Content="切换界面" Command="{Binding SwitchCommand}" Style="{StaticResource MaterialDesignRaisedButton}" Background="{StaticResource PrimaryHueMidBrush}"/> <ContentControl Grid.Row="1" Content="{Binding CurrentViewModel}"/> </Grid> ``` ```csharp // ViewModel public class MainViewModel : INotifyPropertyChanged { private object _currentVM; public object CurrentViewModel { get => _currentVM; set { _currentVM = value; OnPropertyChanged(); } } public ICommand SwitchCommand => new RelayCommand(() => { CurrentViewModel = CurrentViewModel is ViewModelA ? new ViewModelB() : new ViewModelA(); }); } ``` #### 方法三:Frame+Page导航(支持历史记录) ```xml <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <Button Content="界面A" Click="NavToPageA" Margin="5" Style="{StaticResource NavButtonStyle}"/> <Button Content="界面B" Click="NavToPageB" Margin="5" Style="{StaticResource NavButtonStyle}"/> </StackPanel> <Frame Grid.Row="1" x:Name="mainFrame" NavigationUIVisibility="Hidden" ContentRendered="Frame_ContentRendered"/> </Grid> ``` ```csharp // 初始化导航 private void Window_Loaded(object sender, RoutedEventArgs e) { mainFrame.Navigate(new PageA()); } // 导航处理方法 private void NavToPageA(object sender, RoutedEventArgs e) => mainFrame.Navigate(new PageA()); private void NavToPageB(object sender, RoutedEventArgs e) => mainFrame.Navigate(new PageB()); ``` #### 样式优化(参考引用[1]) ```xml <ResourceDictionary> <!-- 按钮样式 --> <Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> <Setter Property="Background" Value="#FF2196F3"/> <Setter Property="Foreground" Value="White"/> <Setter Property="Margin" Value="10"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" CornerRadius="4"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <!-- 页面切换动画 --> <Style x:Key="FadeContent" TargetType="ContentControl"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <ContentPresenter Content="{TemplateBinding Content}"> <ContentPresenter.Triggers> <EventTrigger RoutedEvent="ContentControl.Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:0.3" Storyboard.TargetProperty="Opacity" From="0" To="1"/> </Storyboard> </BeginStoryboard> </EventTrigger> </ContentPresenter.Triggers> </ContentPresenter> </DataTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary> ``` ### 实现方式对比 | 方法 | 优点 | 适用场景 | |-----------|-----------------------------|----------------------| | Visibility| 实现简,直接操作控件可见性 | 快速原型开发、简界面切换 | | DataTemplate | 完全遵循MVVM模式,解耦视图与业务逻辑 | 中大型项目、需要严格分离关注点的场景 | | Frame导航 | 自带导航历史记录,支持前进后退功能 | 需要浏览器式导航体验的应用程序 | ### 常见问题处理 1. **界面切换卡顿** 添加过渡动画提升用户体验: ```xml <ContentControl Content="{Binding CurrentView}"> <ContentControl.Style> <Style TargetType="ContentControl"> <Setter Property="ContentTemplate" Value="{StaticResource FadeContent}"/> </Style> </ContentControl.Style> </ContentControl> ``` 2. **保持界面状态** 推荐使用`KeepAlive`模式: ```csharp mainFrame.Navigate(new PageA(), null, true); // 保持页面实例 ``` 3. **参数传递** 使用`NavigationService`传递参数: ```csharp mainFrame.Navigate(new PageB() { DataContext = new { UserID = 123 } }); ``` --- ### 相关问题 1. 如何实现WPF界面切换时的淡入淡出动画? 2. 使用Frame导航时如何保持页面状态? 3. MVVM模式下如何实现无代码后台的界面切换? 4. 如何通过URL路由实现WPF页面导航? 5. 界面切换时如何实现数据预加载? [^1]: XAML资源字典样式定义规范 [^2]: Prism框架导航服务实现原理 [^3]: WPF页面导航最佳实践
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值