UILayer的一些属性

iOS创建UIView是直接在Core Animation layers上进行绘制,每一个UIView实例都会自动创建CALayer的实例,并且设置为UIView实例的layer属性。

以下事例通过设置UIImageView的layer,实现阴影、圆角、边框和旋转缩放等效果


-(void)initImgView{
    UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logo.jpg"]];
    imgView.frame = CGRectMake((kScroolWidth-200)/2, (kScroolHeight-200)/2, 200, 200);
    [self.view addSubview:imgView];
    //1.设置阴影
    imgView.layer.shadowColor = [UIColor greenColor].CGColor;//阴影颜色
    imgView.layer.shadowOffset = CGSizeMake(10, 10);//阴影偏移量
    imgView.layer.shadowOpacity = 0.5;//阴影不透明度
    
    //2.设置圆角
//    imgView.layer.cornerRadius = 10;//圆角半径
//    imgView.layer.masksToBounds = YES;//强制内部所有子层支持圆角效果
    
    //3.设置边框
//    imgView.layer.borderWidth = 5;//边框大小
//    imgView.layer.borderColor = [UIColor redColor].CGColor;//边框颜色
    
    //4.旋转
//    imgView.layer.transform = CATransform3DMakeRotation(M_PI_4, 1, 1, 0);//旋转,参数(旋转角度,x轴,y轴,z轴),此处是绕着x、y轴旋转45度
    
    //5.缩放
//    imgView.layer.transform = CATransform3DMakeScale(0.5, 1, 0);//x缩放0.5倍,y扩大1倍,z不变
}

1.设置阴影效果


2.设置圆角效果



3.设置边框


4.旋转


5.缩放


<think>我们正在设计一个Unity UI系统,要求支持页面栈管理和弹窗堆叠功能,同时支持界面入栈隐藏和同时显示(即多个界面可以同时显示,比如一个主界面和一个弹窗)。我们还需要考虑优化方案和注意事项。 设计思路: 1. 界面层级管理:我们将界面分为不同的层级(例如:背景层、普通页面层、弹窗层、引导层、顶层等),每个层级用一个独立的Canvas管理,以控制渲染顺序。 2. 页面栈管理:对于普通页面,我们使用栈结构来管理,当打开新页面时,当前页面入栈并隐藏(或根据配置决定是否隐藏);关闭页面时,从栈中弹出上一个页面并显示。 3. 弹窗堆叠:弹窗不使用栈管理,而是使用列表(或堆栈)来记录打开的顺序,弹窗可以同时显示多个,但需要根据打开顺序调整层级(后打开的弹窗显示在上面)。 4. 界面状态管理:每个界面都有其状态(打开中、打开完成、隐藏、关闭中等),需要设计状态机来管理。 5. 优化:包括对象池管理界面实例、避免频繁重建、Canvas分块、动静分离等。 具体实现: 一、层级定义(示例): 我们定义几个层级(可以扩展): - HUD (0) // 常驻信息,如血量、金币 - Scene (100) // 场景UI,如3D物体上的UI - Normal (200) // 普通页面 - PopUp (300) // 弹窗 - Guide (400) // 引导层 - Top (500) // 顶层,如系统弹窗、断线重连 每个层级对应一个Canvas,并设置其sorting order。每个层级的Canvas可以设置不同的渲染模式。 二、界面基类(BaseView): 每个界面都继承自BaseView,包含以下基本方法: - OnEnter(): 进入界面(打开) - OnExit(): 退出界面(关闭) - OnPause(): 暂停(被其他页面覆盖,比如新页面打开时,当前页面会调用OnPause) - OnResume(): 恢复(覆盖的页面关闭,当前页面重新成为栈顶) 三、页面栈管理(PageStack): 管理普通页面(Normal层)的栈。每个页面打开时,先暂停当前栈顶页面,然后将新页面压栈并显示。关闭页面时,弹出当前页面,恢复下一个页面。 四、弹窗管理(PopUpManager): 管理弹窗(PopUp层)。弹窗打开时,添加到弹窗列表,并显示在PopUp层级。弹窗关闭时,从列表中移除。弹窗可以同时显示多个,后打开的弹窗显示在前面(通过调整Canvas的sorting order或者设置SiblingIndex)。 五、界面管理器(UIManager): 单例类,统一管理所有界面的打开、关闭。负责调用页面栈和弹窗管理。 六、优化方案: 1. 对象池:界面关闭后不立即销毁,放入对象池,下次打开时复用。 2. 动静分离:将界面中频繁变化的元素和静态元素分离到不同的Canvas,避免整个界面重绘。 3. 避免频繁激活/禁用GameObject:使用Canvas Group控制交互和透明度,而不是SetActive。 4. 分帧加载:对于复杂的界面,分帧加载其内容,避免卡顿。 5. 资源卸载:对于不再使用的界面,及时卸载其资源。 七、注意事项: 1. 内存管理:注意界面缓存和资源释放,避免内存泄漏。 2. 事件穿透:弹窗打开时,需要阻止下层界面的事件(使用Canvas Group的interactable和blocksRaycasts)。 3. 循环引用:在界面关闭时,确保移除所有事件监听,避免内存泄漏。 4. 界面生命周期:确保界面在正确的时机执行正确的生命周期方法。 5. 背景模糊:弹窗可能需要背景模糊效果,可以通过截屏后处理实现,但要注意性能。 代码结构示例: 1. 定义层级枚举: ```csharp public enum UILayer { HUD = 0, Scene = 100, Normal = 200, PopUp = 300, Guide = 400, Top = 500 } ``` 2. 界面基类: ```csharp public abstract class BaseView : MonoBehaviour { public UILayer Layer { get; set; } public abstract void OnEnter(); public abstract void OnExit(); public abstract void OnPause(); public abstract void OnResume(); } ``` 3. 页面栈(管理Normal层): ```csharp public class PageStack { private Stack<BaseView> stack = new Stack<BaseView>(); public void Push(BaseView view) { if (stack.Count > 0) { var top = stack.Peek(); top.OnPause(); } stack.Push(view); view.OnEnter(); } public void Pop() { if (stack.Count == 0) return; var top = stack.Pop(); top.OnExit(); if (stack.Count > 0) { var next = stack.Peek(); next.OnResume(); } } } ``` 4. 弹窗管理器: ```csharp public class PopUpManager { private List<BaseView> popups = new List<BaseView>(); public void ShowPopup(BaseView popup) { popups.Add(popup); popup.OnEnter(); // 调整弹窗的层级(比如设置SiblingIndex,让后打开的显示在上面) AdjustPopupOrder(); } public void ClosePopup(BaseView popup) { popup.OnExit(); popups.Remove(popup); AdjustPopupOrder(); } private void AdjustPopupOrder() { // 按照打开顺序调整层级,后打开的显示在上面 for (int i = 0; i < popups.Count; i++) { popups[i].transform.SetSiblingIndex(i); } } } ``` 5. UI管理器(单例): ```csharp public class UIManager : MonoBehaviour { public static UIManager Instance; private Dictionary<UILayer, Transform> layerRoots = new Dictionary<UILayer, Transform>(); private PageStack pageStack = new PageStack(); private PopUpManager popUpManager = new PopUpManager(); void Awake() { Instance = this; // 初始化各个层级的Canvas foreach (UILayer layer in Enum.GetValues(typeof(UILayer))) { GameObject go = new GameObject(layer.ToString()); go.transform.SetParent(transform); Canvas canvas = go.AddComponent<Canvas>(); canvas.overrideSorting = true; canvas.sortingOrder = (int)layer; go.AddComponent<GraphicRaycaster>(); layerRoots[layer] = go.transform; } } public void ShowPage(BaseView page) { page.transform.SetParent(layerRoots[UILayer.Normal]); pageStack.Push(page); } public void ClosePage() { pageStack.Pop(); } public void ShowPopup(BaseView popup) { popup.transform.SetParent(layerRoots[UILayer.PopUp]); popUpManager.ShowPopup(popup); } public void ClosePopup(BaseView popup) { popUpManager.ClosePopup(popup); } } ``` 八、扩展考虑: 1. 界面配置:每个界面可以配置自己的属性(如背景模糊、是否全屏、是否阻断下层交互等)。 2. 动画:支持打开和关闭动画,需要在动画完成后才执行状态切换。 3. 音效:打开和关闭界面时播放音效。 4. 背景点击关闭:弹窗支持点击背景关闭。 以上是一个基本的设计框架,具体实现时还需要根据项目需求调整。 注意事项: - 在Unity中,Canvas的渲染顺序由sortingOrder决定,同一Canvas下的子物体由SiblingIndex决定(从0开始,越大越后渲染,显示在更上面)。 - 弹窗堆叠时,我们通常将后打开的弹窗放在前面(通过设置SiblingIndex为最后,即transform.SetAsLastSibling())。 优化点补充: - 对于频繁更新的UI元素(如血条、分数),可以单独放在一个Canvas,并设置Canvas的OverrideSorting和SortingOrder,同时将其他静态元素放在另一个Canvas,减少重绘。 - 使用AssetBundle加载界面资源,实现按需加载和卸载。 引用中的一些设计思路也融入到了我们的方案中,例如: - 层级管理(引用[2]) - 界面生命周期(引用[2]) - 事件系统(引用[1]) 最后,我们还需要编写一个界面配置工具,方便配置每个界面的属性(如层级、背景类型、是否阻断等)。 根据以上设计,我们可以实现一个支持页面栈管理和弹窗堆叠的UI系统。</think>### Unity UI系统设计:页面栈管理与弹窗堆叠实现 #### 一、核心架构设计 1. **层级系统**(每个层级独立Canvas) ```csharp public enum UILayer { HUD = 0, // 常驻层(排序值0-99) SceneUI = 100, // 场景UI层 NormalPage = 200, // 普通页面层(栈管理) PopUp = 300, // 弹窗层(堆叠管理) Guide = 400, // 引导层 Top = 500 // 顶层(最高优先级) } ``` 2. **界面状态机** ```mermaid stateDiagram-v2 [*] --> Loading Loading --> Active: 加载完成 Active --> Hidden: 被新界面覆盖 Hidden --> Active: 重新激活 Active --> Closing: 关闭触发 Closing --> [*]: 销毁/缓存 ``` #### 二、页面栈管理实现 ```csharp public class PageStackManager { private Stack<BasePage> pageStack = new Stack<BasePage>(); public void OpenPage(BasePage newPage) { if (pageStack.Count > 0) { var topPage = pageStack.Peek(); topPage.SetState(PageState.Hidden); // 暂停当前页 } newPage.SetState(PageState.Active); pageStack.Push(newPage); } public void CloseCurrentPage() { if (pageStack.Count == 0) return; var closingPage = pageStack.Pop(); closingPage.SetState(PageState.Closing); if (pageStack.Count > 0) { var prevPage = pageStack.Peek(); prevPage.SetState(PageState.Active); // 恢复上一页 } } } ``` #### 三、弹窗堆叠系统 ```csharp public class PopupManager { private List<BasePopup> activePopups = new List<BasePopup>(); public void ShowPopup(BasePopup popup) { // 设置层级:基础值 + 弹窗数量 * 间隔 int sortOrder = (int)UILayer.PopUp + activePopups.Count * 10; popup.SetSortOrder(sortOrder); activePopups.Add(popup); popup.SetState(PopupState.Active); } public void ClosePopup(BasePopup popup) { popup.SetState(PopupState.Closing); activePopups.Remove(popup); RefreshPopupDepths(); // 重新计算剩余弹窗层级 } private void RefreshPopupDepths() { for (int i = 0; i < activePopups.Count; i++) { activePopups[i].SetSortOrder((int)UILayer.PopUp + i * 10); } } } ``` #### 四、关键优化方案 1. **渲染性能优化** - 每个界面独立Canvas,避免合批打断 - 静态元素使用`Canvas`+`GraphicRaycaster`,动态元素分离子Canvas - 禁用不可见界面的`RaycastTarget` 2. **对象池管理** ```csharp public class ViewPool { private Dictionary<string, Queue<BaseView>> pools = new Dictionary<string, Queue<BaseView>>(); public BaseView GetView(string prefabPath) { if (!pools.ContainsKey(prefabPath)) pools[prefabPath] = new Queue<BaseView>(); if (pools[prefabPath].Count > 0) { return pools[prefabPath].Dequeue().Activate(); } return Instantiate(Resources.Load<BaseView>(prefabPath)); } public void ReturnView(BaseView view) { view.Deactivate(); pools[view.PrefabPath].Enqueue(view); } } ``` 3. **事件穿透处理** - 弹窗打开时自动添加阻挡层 - 使用`EventSystem.current.IsPointerOverGameObject()`检测点击穿透 #### 五、注意事项 1. **生命周期管理** - 页面关闭时注销所有事件监听 - 使用`CancellationToken`取消异步加载任务 - 场景切换时清空非持久化栈 2. **内存优化** - 大纹理使用`Addressables`按需加载 - 超过2分钟未访问的界面自动卸载资源 - 定期调用`Resources.UnloadUnusedAssets()` 3. **弹窗堆叠限制** ```csharp // 弹窗数量限制 if (activePopups.Count >= MAX_POPUP_COUNT) { autoCloseOldestPopup(); } ``` 4. **背景模糊优化** - 使用`RenderTexture`+`BlurMaterial`预生成模糊背景 - 相同背景的弹窗共享模糊纹理 - 动态调整模糊强度:`material.SetFloat("_BlurSize", blurLevel);` #### 六、界面配置示例 ```csharp [CreateAssetMenu(fileName = "PageConfig", menuName = "UI/Page Config")] public class PageConfig : ScriptableObject { public UILayer layer = UILayer.NormalPage; public bool destroyWhenClosed = false; public bool blockLowerLayer = true; public AudioClip openSound; public AudioClip closeSound; [Range(0, 1)] public float bgAlpha = 0.7f; } ``` > **最佳实践建议**: > 1. 使用`CanvasGroup`控制透明度替代SetActive(false) > 2. 复杂界面采用分帧加载:`yield return null` > 3. 高频更新数据使用UniRx/Observable优化 > 4. 编辑器扩展界面配置工具[^2][^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值